Thứ Bảy, 5 tháng 1, 2008

Intelligent Connect in DirectShow

Intelligent Connect

Intelligent Connect is the mechanism the Filter Graph Manager uses to build filter graphs. It consists of several related algorithms that select filters and add them to the filter graph. For application programming, you rarely need to know the details of Intelligent Connect. Read this section if you are having trouble building a certain filter graph and want to troubleshoot the problem, or if you are writing your own filter and want to make it available for automatic graph building.

Intelligent Connect involves the following IGraphBuilder methods:

  • IGraphBuilder::Render
  • IGraphBuilder::AddSourceFilter
  • IGraphBuilder::RenderFile
  • IGraphBuilder::Connect

The Render method builds a subsection of a graph. It starts from an unconnected output pin and works downstream, adding new filters as needed. The starting filter must already be in the graph. At each step, the Render method searches for a filter that can connect to the previous filter. The stream can branch if a connecting filter has multiple output pins. The search stops when every stream has a renderer. If the Render method gets stuck, it might back up and try again, using a different set of filters.

To connect each output pin, the Render method does the following:

  1. If the pin supports the IStreamBuilder interface, the Filter Graph Manager delegates the entire process to the pin's IStreamBuilder::Render method. By exposing this interface, the pin assumes responsibility for building the remainder of the graph, down to the renderer. However, very few pins support this interface.
  2. The Filter Graph Manager tries to use filters that are cached in memory, if any. Throughout the Intelligent Connect process, the Filter Graph Manager may cache filters from earlier steps in the process.
  3. If the filter graph contains any filters with unconnected input pins, the Filter Graph Manager tries them next. You can force the Render method to try a particular filter by adding that filter to the graph before calling Render.
  4. Finally, the Filter Graph Manager searches the registry, using the IFilterMapper2::EnumMatchingFilters method. It tries to match the output pin's preferred media types against media types listed in the registry.

Each filter is registered with a merit, a numerical value that indicates how preferable the filter is, relative to other filters. The EnumMatchingFilters method returns filters in order of merit, with a minimum merit of MERIT_DO_NOT_USE + 1. It ignores filters with a merit of MERIT_DO_NOT_USE or less. Filters are also grouped into categories, defined by GUID. Categories themselves have merit, and the EnumMatchingFilters method ignores any category with a merit of MERIT_DO_NOT_USE or less, even if the filters in that category have higher merit values.

To summarize, the Render method tries filters in the following order:

  1. Use IStreamBuilder.
  2. Try cached filters.
  3. Try filters in the graph.
  4. Look up filters in the registry.

The AddSourceFilter method adds a source filter that can render a specified file. First it looks in the registry and matches against the protocol (such as http://), the file extension, or a set of predetermined check bytes, which are bytes at particular offsets in the file that match certain patterns. For details, see Registering a Custom File Type. Assuming that the method locates an appropriate source filter, it then creates an instance of that filter, adds it to the graph, and calls the filter's IFileSourceFilter::Load method with the file name.

The RenderFile method builds a default playback graph from a file name. Internally, this method uses AddSourceFilter to locate the correct source filter, and Render to build the rest of the graph.

The Connect method connects and output pin to an input pin. This method adds intermediate filters if needed, using a variation of the algorithm described for the Render method:

  1. Try a direct connection between the filters, with no intermediate filters.
  2. Try cached filters.
  3. Try filters in the graph.
  4. Look up filters in the registry.

Understanding the Intelligent Connect

See how the graphbuilder work :

i'll implement how to "connect intelligent" in our Delphi application.


Step 1 : Add Source Filter

var pSource : IBaseFilter;
pGraph.AddSourceFilter('c:\example.avi', 'Source File (Async)', pSource);

There 2 way to connect between filter :

  • Direct Connect : the graph connect directly 2 "pin" & failed if they can't connect
  • Try Connect : the graph connect it directly & if failed the graph automatically find other filter for make right connect.'
Step 2 : Add the renderer Filter
i usually use this function to add any filter to my graph


function AddFilter(pGraph : IGraphBuilder;GUID: TGUID; Name : WideString; out Filter : IBaseFilter): HResult;
var hr : HRESULT;
begin
Result := S_FALSE;
if not Assigned(pGraph) then exit;

hr := CoCreateInstance(GUID, nil, CLSCTX_INPROC_SERVER, IID_IBaseFilter, Filter);

if SUCCEEDED(hr) then
begin
hr := pGraph.AddFilter(Filter, PWideChar(Name));
if Failed(hr) then Filter._Release;
end;
result := hr;
end;

You'll need to add the VideoRenderer (For video output) & the Default DirectSound Device (for sound output)


hr := AddFilter(Graph, CLSID_VideoRendererDefault, 'Video Renderer', pVideo);
hr := AddFilter(Graph, CLSID_DSoundRender, 'Default DirectSound Device', pAudio);


Step 3 : Connect the source to the other filter

function GetUnconnectedPin(pFilter: IBaseFilter; PinDir: TPinDirection; out ppPin: IPin): HRESULT;
var
pEnum : IEnumPins;
hr : HRESULT;
pPin, pTmp : Ipin;
ThisPinDir : PIN_DIRECTION;
found : boolean;
begin
found := false;
ppPin := nil;
pEnum := nil;
pPin := nil;
hr := pFilter.EnumPins(pEnum);
if failed(hr) then
begin
result := hr;
exit;
end;
while pEnum.Next(1, pPin, NIL) = S_OK do
begin
pPin.QueryDirection(ThisPinDir);
if (ThisPinDir = PinDir) then
begin
pTmp := nil;
hr := pPin.ConnectedTo(pTmp);
if SUCCEEDED(hr) then // Already connected, not the pin we want.
pTmp := nil
else // Unconnected, this is the pin we want.
begin
found := true;
ppPin := pPin;
break;
end;
end;
pPin := nil;
end;
pEnum := nil;
if found then
result := S_OK
else
result := E_FAIL;
end;
var inpin, outpin : IPIN;
if Succeeded(GetUnconnectedPin(pVideo, PINDIR_INPUT, ip)) then
if Succeeded(GetUnconnectedPin(pSource, PINDIR_OUTPUT, op)) then
if Succeeded(Graph.Connect(op, ip)) then
// we have connect the graph like this :
// Source -> AVI Splitter -> Avi Decoder -> Video Renderer


the final step is connect to sound

function ConnectToSound(pGraph : IGraphBuilder; AudioFilter : IBaseFilter) : HResult;
var FL : TFilterList;
// PL : TPinList;
hr : HResult;
i : integer;
iP, oP : IPIN;
pEnum : IEnumPins;
ThisPinDir : PIN_DIRECTION;
pTmp : Ipin;
begin
hr := GetUnconnectedPin(AudioFilter, PINDIR_INPUT, iP);
if Failed(hr) then
begin
result := hr;
exit;
end;
FL := TFilterList.Create(pGraph);
for i := 0 to FL.Count - 1 do
if FL[i] <> AudioFilter then
begin
hr := FL[i].EnumPins(pEnum);
if Windows.Succeeded(hr) then
begin
while pEnum.Next(1, op, NIL) = S_OK do
begin
op.QueryDirection(ThisPinDir);
if (ThisPinDir = PinDir_OUTPUT) then
begin
pTmp := nil;
hr := op.ConnectedTo(pTmp);
if SUCCEEDED(hr) then // Already connected, not the pin we want.
pTmp := nil
else // Unconnected, this is the pin we want.
begin
hr := pGraph.Connect(op, ip);
if Windows.Succeeded(hr) then
begin
FL.Free;
result := hr;
exit;
end
else op := nil;
end;
end;
end;
end;
end;
FL.Free;
result := -1;
end;



call :
ConnectToSound(pGraph, pAudio);

And it done :D

Why use intelligent conenct ?
Use intelligent connect in case we want to connect our own filter or connect in our way. In my work, i use intelligent to connect my audio filter between Audio Decoder & Audio Renderer.

Conclusion
If you don't have anything special you should use the normal way to connect the graph :
+ Add Filter you want to the Graph
+ Call IGraphBuilder.RenderFile

Không có nhận xét nào: