|Writing to a File|
Writing an AVI FileIf we take a look at the ICaptureGraphBuilder2 interface at the msdn site, in addition to the RenderStream method we can see something else that looks useful -- SetOutputFileName.
Let's take a look at it in Delphi and compare it with the notes at the msdn site:
It takes four parameters.
The first specifies the Media SUBTYPE and can be either of the following:
The second parameter is a PWideChar pointing to the filename.
The third and fourth parameters are interesting because they're values returned from the function.
When we call SetOutputFileName, it creates a multiplexer filter based on the value of the first parameter. For AVI, it creates the AVI Multiplexer Filter. For ASF, it creates the WM ASF Writer. It adds the multiplexer to the filter graph, and returns a pointer to its IBaseFilter interface in the third parameter.
The final parameter returns as a pointer to the file writer's IFileSinkFilter interface.
So, we know, from that parameter list, we need to specify a filename for the file we're going to write. We know, from the third parameter, that we need to declare a variable (of type IBaseFilter) which SetOutputFileName will use to return to us a pointer to the Multiplexer it creates. And we know, from the fourth parameter, we need to declare another variable (of type IFileSinkFilter) for a pointer to the file-writer itself.
Lets add an OpenDialog component to the Form, add a few variable declarations to our project's ListBox1Click procedure and add a line or two of code to get ourselves a filename.
procedure TForm1.ListBox1Click(Sender: TObject); var CaptureFile: WideString; Multiplexer: IBaseFilter; Writer: IFileSinkFilter; begin if OpenDialog1.Execute then CaptureFile:= OpenDialog1.FileName;
We can now build the Filter Graph using FilterGraph1 as ICaptureGraphBuilder2 as before but we can include a call to SetOutputFileName to create the Multiplexer and set up the file-writer. Then we can connect the video source filter's CAPTURE pin to the Multiplexer that was created by the SetoutputFilename call.
with FilterGraph1 as ICaptureGraphBuilder2 do begin SetOutputFileName(MEDIASUBTYPE_Avi, PWideChar(CaptureFile), Multiplexer, Writer); RenderStream(@PIN_CATEGORY_CAPTURE, nil, Filter1 as IBaseFilter, nil, Multiplexer as IBaseFilter);
Ok, here's the complete procedure for the ListBox1Click event. The extra code needed to write the file is shown in dark blue:
procedure TForm1.ListBox1Click(Sender: TObject);var
CaptureFile: WideString; Multiplexer: IBaseFilter; Writer: IFileSinkFilter; begin if OpenDialog1.Execute then CaptureFile:= OpenDialog1.FileName; if FilterGraph1.Active then FilterGraph1.Active:= False; FilterGraph1.ClearGraph; if ListBox1.ItemIndex > -1 then begin Filter1.BaseFilter.Moniker:= SysDev.GetMoniker(ListBox1.ItemIndex); FilterGraph1.Active:= True; FilterGraph1.Mode:= gmCapture; with FilterGraph1 as ICaptureGraphBuilder2 do begin SetOutputFileName(MEDIASUBTYPE_Avi, PWideChar(CaptureFile), Multiplexer, Writer); RenderStream(@PIN_CATEGORY_CAPTURE, nil, Filter1 as IBaseFilter, nil, Multiplexer as IBaseFilter); RenderStream(@PIN_CATEGORY_PREVIEW, nil, Filter1 as IBaseFilter, nil, VideoWindow1 as IBaseFilter); end; FilterGraph1.Play; end; end;
Run the project and enter a filename with the extension .avi - we've hard-coded the media subtype as MEDIASUBTYPE_Avi; a more "robust" program would, of course, check the filename extension. Run GraphEdit and connect to our Filter Graph. You should see something like this;
So, why use the Multiplexer? Well, AVI does mean Audio-Video-Interleaved so I guess it's reasonable for SetOutputFilename to assume that audio was going to be added to the filter graph.