I used search on StackOverflow, but I didn't find the answer. I'm developing an application and use OpenCV, but I need to work with different videos (mostly *.avi), so I decided to use DirectShow. I was able to create simple application, but I can't find any explanation how to get frames from *.avi, without creation of ActiveWindow. In fact, I need only to read video with DirectShow and then I'll use OpenCV to process and show video. Any help appreciated. Thanks in advance!
Excuse me for my awful English.
Create a graph with a NULL render. Also look at sample grabber example in directshow SDK. It shows how to grab a frame for a graph. You can then pass on the frame to openCV for processing.
Basically you want to connect something like this:
Source -> Sample Grabber -> Null renderer
Download graphEdit or GraphEdit+ and you can visually represent these filters. As an example I went ahead and built a graph from my local webcam to a sample grabber connectted to a null renderer. The C# code generated by GraphEdit+ is this:
//Don't forget to add reference to DirectShowLib in your project.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices;
using DirectShowLib;
namespace graphcode
{
class Program
{
static void checkHR(int hr, string msg)
{
if (hr < 0)
{
Console.WriteLine(msg);
DsError.ThrowExceptionForHR(hr);
}
}
static void BuildGraph(IGraphBuilder pGraph)
{
int hr = 0;
//graph builder
ICaptureGraphBuilder2 pBuilder = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
hr = pBuilder.SetFiltergraph(pGraph);
checkHR(hr, "Can't SetFiltergraph");
Guid CLSID_SampleGrabber = new Guid("{C1F400A0-3F08-11D3-9F0B-006008039E37}"); //qedit.dll
Guid CLSID_NullRenderer = new Guid("{C1F400A4-3F08-11D3-9F0B-006008039E37}"); //qedit.dll
//add Integrated Camera
IBaseFilter pIntegratedCamera = CreateFilter(@"@device:pnp:\\?\usb#vid_04f2&pid_b221&mi_00#7&34997cec&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global");
hr = pGraph.AddFilter(pIntegratedCamera, "Integrated Camera");
checkHR(hr, "Can't add Integrated Camera to graph");
//add SampleGrabber
IBaseFilter pSampleGrabber = (IBaseFilter)Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID_SampleGrabber));
hr = pGraph.AddFilter(pSampleGrabber, "SampleGrabber");
checkHR(hr, "Can't add SampleGrabber to graph");
//add Null Renderer
IBaseFilter pNullRenderer = (IBaseFilter)Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID_NullRenderer));
hr = pGraph.AddFilter(pNullRenderer, "Null Renderer");
checkHR(hr, "Can't add Null Renderer to graph");
//connect Integrated Camera and SampleGrabber
hr = pGraph.ConnectDirect(GetPin(pIntegratedCamera, "Capture"), GetPin(pSampleGrabber, "Input"), null);
checkHR(hr, "Can't connect Integrated Camera and SampleGrabber");
//connect SampleGrabber and Null Renderer
hr = pGraph.ConnectDirect(GetPin(pSampleGrabber, "Output"), GetPin(pNullRenderer, "In"), null);
checkHR(hr, "Can't connect SampleGrabber and Null Renderer");
}
static void Main(string[] args)
{
try
{
IGraphBuilder graph = (IGraphBuilder)new FilterGraph();
Console.WriteLine("Building graph...");
BuildGraph(graph);
Console.WriteLine("Running...");
IMediaControl mediaControl = (IMediaControl)graph;
IMediaEvent mediaEvent = (IMediaEvent)graph;
int hr = mediaControl.Run();
checkHR(hr, "Can't run the graph");
bool stop = false;
int n = 0;
while (!stop)
{
System.Threading.Thread.Sleep(500);
Console.Write(".");
EventCode ev;
IntPtr p1, p2;
if (mediaEvent.GetEvent(out ev, out p1, out p2, 0) == 0)
{
if (ev == EventCode.Complete || ev == EventCode.UserAbort)
{
Console.WriteLine("Done!");
stop = true;
}
else
if (ev == EventCode.ErrorAbort)
{
Console.WriteLine("An error occured: HRESULT={0:X}", p1);
mediaControl.Stop();
stop = true;
}
mediaEvent.FreeEventParams(ev, p1, p2);
}
// stop after 10 seconds
n++;
if (n > 20)
{
Console.WriteLine("stopping..");
mediaControl.Stop();
stop = true;
}
}
}
catch (COMException ex)
{
Console.WriteLine("COM error: " + ex.ToString());
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.ToString());
}
}
public static IBaseFilter CreateFilter(string displayName)
{
int hr = 0;
IBaseFilter filter = null;
IBindCtx bindCtx = null;
IMoniker moniker = null;
try
{
hr = CreateBindCtx(0, out bindCtx);
Marshal.ThrowExceptionForHR(hr);
int eaten;
hr = MkParseDisplayName(bindCtx, displayName, out eaten, out moniker);
Marshal.ThrowExceptionForHR(hr);
Guid guid = typeof(IBaseFilter).GUID;
object obj;
moniker.BindToObject(bindCtx, null, ref guid, out obj);
filter = (IBaseFilter)obj;
}
finally
{
if (bindCtx != null) Marshal.ReleaseComObject(bindCtx);
if (moniker != null) Marshal.ReleaseComObject(moniker);
}
return filter;
}
[DllImport("ole32.dll")]
public static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);
[DllImport("ole32.dll")]
public static extern int MkParseDisplayName(IBindCtx pcb, [MarshalAs(UnmanagedType.LPWStr)] string szUserName, out int pchEaten, out IMoniker ppmk);
static IPin GetPin(IBaseFilter filter, string pinname)
{
IEnumPins epins;
int hr = filter.EnumPins(out epins);
checkHR(hr, "Can't enumerate pins");
IntPtr fetched = Marshal.AllocCoTaskMem(4);
IPin[] pins = new IPin[1];
while (epins.Next(1, pins, fetched) == 0)
{
PinInfo pinfo;
pins[0].QueryPinInfo(out pinfo);
bool found = (pinfo.name == pinname);
DsUtils.FreePinInfo(pinfo);
if (found)
return pins[0];
}
checkHR(-1, "Pin not found");
return null;
}
}
}
You'd still need to actually capture the sample frame, but as was mentioned in the post above you can look into the sampleGrabber SDK on MSDN to find out how to do that.