I've been working on an application that uses Directshow.NET, and decided to start tinkering with GraphEd.exe to figure out the exact route I need to take.
The simplest version of what I want is in the image above (where "test.mpeg" is a "File writer" filter).
Sure enough, what looks easy in GraphEd is not as simple as coding it. After a few seconds, I realized that directshow is something I just don't understand. Here is my C# code attempt:
int hr;
IBaseFilter cameraStream = null;
IBaseFilter mpegEncoder = null;
IBaseFilter fileWriter = null;
ICaptureGraphBuilder2 capGraph = null;
filterGraph = (IFilterGraph2)new FilterGraph();
try
{
capGraph = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
hr = capGraph.SetFiltergraph(filterGraph);
Marshal.ThrowExceptionForHR(hr);
hr = filterGraph.AddSourceFilterForMoniker(dev.Mon, null, dev.Name, out cameraStream);
Marshal.ThrowExceptionForHR(hr);
IPin cameraData = null;
hr = capGraph.FindPin(cameraStream, PinDirection.Output, PinCategory.Capture, MediaType.Video, true, 0, out cameraData );
//hr = cameraStream.FindPin("Capture", out cameraData);
Marshal.ThrowExceptionForHR(hr);
mpegEncoder = (IBaseFilter)new MJPGEnc();
hr = filterGraph.AddFilter(mpegEncoder, "mpeg encoder");
//hr = filterGraph.FindFilterByName("Microsoft MPEG-2 Video Encoder", out mpegEncoder);
Marshal.ThrowExceptionForHR(hr);
IPin mpegInput = null;
//hr = mpegEncoder.FindPin("Input0", out mpegInput);
hr = capGraph.FindPin(mpegEncoder, PinDirection.Input, null, null, true, 0, out mpegInput);
Marshal.ThrowExceptionForHR(hr);
filterGraph.Connect(cameraData, mpegInput);
IPin mpegOutput = null;
//hr = mpegEncoder.FindPin("Output", out mpegOutput);
hr = capGraph.FindPin(mpegEncoder, PinDirection.Output, null, null, true, 0, out mpegOutput);
Marshal.ThrowExceptionForHR(hr);
//hr = filterGraph.FindFilterByName("File writer", out fileWriter);
fileWriter = (IBaseFilter)new FileWriter();
IFileSinkFilter test = fileWriter as IFileSinkFilter2;
AMMediaType mtype = new AMMediaType();
mtype.majorType = MediaType.Video;
mtype.subType = MediaSubType.RGB24;
mtype.formatPtr = IntPtr.Zero;
test.SetFileName(videoPath, mtype );
IPin fwriterIn = null;
//hr = fileWriter.FindPin("in", out fwriterIn);
hr = capGraph.FindPin(fileWriter, PinDirection.Input, null, null, true, 0, out fwriterIn);
Marshal.ThrowExceptionForHR(hr);
filterGraph.Connect(mpegOutput, fwriterIn);
hr = capGraph.RenderStream(null, null, cameraStream, null, fileWriter); // *** Breaks on this line! ***
Marshal.ThrowExceptionForHR(hr);
mediaControl = filterGraph as IMediaControl;
}
finally
{
if (cameraStream != null)
{
Marshal.ReleaseComObject(cameraStream);
cameraStream = null;
}
if (mpegEncoder != null)
{
Marshal.ReleaseComObject(mpegEncoder);
mpegEncoder = null;
}
if (fileWriter != null)
{
Marshal.ReleaseComObject(fileWriter);
fileWriter = null;
}
if (capGraph != null)
{
Marshal.ReleaseComObject(capGraph);
capGraph = null;
}
}
After writing and looking at how ridiculous my code was (as far as programming logic is concerned), I realized I needed some help.
Could anyone please direct me to some sort of structured directshow tutorial that explains things to me as if I'm a huge newbie, or possibly link me to some sort of C# library that handles webcam to file recording? I'm not generally a win32 programmer, so anything that eases the pain should be good :)
Also, I do have camera device enumeration code that seems to work:
private void rescan_camera()
{
comboBox1.Items.Clear();
DsDevice[] cameraDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
for(int i=0; i < cameraDevices.Length; i++) {
comboBox1.Items.Add(cameraDevices[i].Name);
}
if (comboBox1.Items.Count > 0) comboBox1.SelectedIndex = 0;
}
I can get the moniker data from a DsDevice, which I know is a key part to telling the directshow filter graph where I want to record from, but that's about the extent of my knowledge.
Edit: Updated code, shows exact place of line break.