Context
I'm trying to apply filter such as contrast, color change, brightness on every frame of a .avi video.
The video is playing just fine with directshow.net and c#.
after a couple hours of research, I found out that buffercb was not the way to go to do the job.
Apparantly, EZrgb24 is a filter I can add to my graph that does exactly what I want.
However, I can't get it to work.
Added in the beggining of my class
[DllImport("ole32.dll", EntryPoint = "CoCreateInstance", CallingConvention = CallingConvention.StdCall)]
static extern UInt32 CoCreateInstance([In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
IntPtr pUnkOuter, UInt32 dwClsContext, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object rReturnedComObject);
Here is relevant code that works
int hr = 0;
IBaseFilter ibfRenderer = null;
ISampleGrabber sampGrabber = null;
IBaseFilter capFilter = null;
IPin iPinInFilter = null;
IPin iPinOutFilter = null;
IPin iPinInDest = null;
Type comType = null;
object comObj = null;
m_FilterGraph = new FilterGraph() as IFilterGraph2;
try
{
// Get the SampleGrabber interface
sampGrabber = new SampleGrabber() as ISampleGrabber;
// Add the video source
hr = m_FilterGraph.AddSourceFilter(_videoPath, "Ds.NET FileFilter", out capFilter);
DsError.ThrowExceptionForHR(hr);
// Hopefully this will be the video pin
IPin iPinOutSource = DsFindPin.ByDirection(capFilter, PinDirection.Output, 0);
IBaseFilter baseGrabFlt = sampGrabber as IBaseFilter;
ConfigureSampleGrabber(sampGrabber);
iPinInFilter = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Input, 0);
iPinOutFilter = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Output, 0);
// Add the frame grabber to the graph
hr = m_FilterGraph.AddFilter(baseGrabFlt, "Ds.NET Grabber");
DsError.ThrowExceptionForHR(hr);
hr = m_FilterGraph.Connect(iPinOutSource, iPinInFilter);
DsError.ThrowExceptionForHR(hr);
// Get the default video renderer
ibfRenderer = (IBaseFilter)new VideoRendererDefault();
// Add it to the graph
hr = m_FilterGraph.AddFilter(ibfRenderer, "Ds.NET VideoRendererDefault");
DsError.ThrowExceptionForHR(hr);
iPinInDest = DsFindPin.ByDirection(ibfRenderer, PinDirection.Input, 0);
// Connect the graph. Many other filters automatically get added here
hr = m_FilterGraph.Connect(iPinOutFilter, iPinInDest);
DsError.ThrowExceptionForHR(hr);
SaveSizeInfo(sampGrabber);
HERE WE WANT TO ADD THE EZRGB FILTER.
Code that doesnt work
/*
// { 8B498501-1218-11cf-ADC4-00A0D100041B }
DEFINE_GUID(CLSID_EZrgb24,
0x8b498501, 0x1218, 0x11cf, 0xad, 0xc4, 0x0, 0xa0, 0xd1, 0x0, 0x4, 0x1b);
*/
unsafe
{
Guid IUnknownGuid = new Guid("00000000-0000-0000-C000-000000000046"); //Can it be written in more pretty style?
Guid ezrgbclsid = new Guid(0x8b498501, 0x1218, 0x11cf, 0xad, 0xc4, 0x0, 0xa0, 0xd1, 0x0, 0x4, 0x1b);
uint hr1 = CoCreateInstance(ezrgbclsid, IntPtr.Zero, (uint)(CLSCTX.CLSCTX_INPROC_HANDLER), ezrgbclsid, out x);//CLSCTX_LOCAL_SERVER
IIPEffect myEffect = (IIPEffect)x;// as IIPEffect;
if (hr1 != 0)
{
int iError = Marshal.GetLastWin32Error();
Console.Write("CoCreateInstance Error = {0}, LastWin32Error = {1}", hr1, iError);
}
myEffect.put_IPEffect(1004, 0, 100); //for this filter, look at resource.h for what the int should be, in this case 1002 is the emboss effect
}
My diagnostic
I found out that the int value returned in hr1, is the hexadecimal value for dll not registred.
Which means to me that EZRGB is not registred on my computer.
How I tryed to solve the problem
Found and downloaded EZRGB.ax on some obscure web site.
executed the command :
cd \windows\syswow64
regsvr32 c:\ezrgb24.ax
A message box appeared with DllRegisterServer in c:\ezrgb24.ax succeeded.
Still doesn't work.
I am using directshow.net, however, this is also tagged both directshow as I feel the solution will work for either c# or c++.
Use can use SampleCB instead of BufferCB; the former provides you access to data which is streamed further, so you can modify it
The typical problem with registration is that you build 32-bit DLL and you are trying to use it from 64-bit code. The bitnesses have to match.
You need CLSCTX_ALL or CLSCTX_INPROC_SERVER
Related
My goal :
Saving all Window Icon Handle(HICON) from inside an HIMAGELIST as multiple image files (.png or .tiff).
My issue :
After my saving procedure some images have poor quality but some don't.
I only noticed this problem on the images of folders with subfolders / subfiles.
My attempt :
Code background:
I'm using Vanara to help me with PInvoke calls and a lot more.
The HIMAGELISTcome from a ListView using the ListViewMessage:LVM_GETIMAGELIST.
This method is part of a Shell Extension (I know, I shouldn't do that).
private void Saving()
{
var hWnd = GetListViewHWnd(); // This is the Desktop SysListView32 HWND
IntPtr lParam = IntPtr.Zero;
IntPtr pHil = SendMessage(hWnd, ListViewMessage.LVM_GETIMAGELIST, 0, ref lParam);
var sHil = new SafeHIMAGELIST(pHil); // This is the IMAGELIST of the ListView
var imageCount = sHil.Interface.GetImageCount(); // sHil.Interface == IImageList Interface
for (int i = 0; i < imageCount; i++)
{
using (var fs = File.OpenWrite(#"C:\Users\Julien\Desktop\Icons\" + i + ".tiff"))
{
using (SafeHICON sHIcon = sHil.Interface.GetIcon(i, IMAGELISTDRAWFLAGS.ILD_NORMAL))
{
var bmpS = Imaging.CreateBitmapSourceFromHIcon(
sHIcon.DangerousGetHandle(),
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
BitmapEncoder enc = new TiffBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bmpS));
enc.Save(fs);
}
}
}
sHil.Dispose();
}
Also :
var bmp = Bitmap.FromHicon(sHIcon.DangerousGetHandle());
bmp.Save(fs);
FAQ :
Why I am using the listview imagelist and not SHGetFileInfo ?
Because SHGetFileInfo will give me an HICON like that :
for a folder that in reality look like this :
What about passing SHGFI_SYSICONINDEX in your SHGetFileInfo ?
Same thing, the icons of non-empty folders is not stored in the System Image List.
Since I can wrote my extension in C++ I am open to any solution written in C++ too.
Edit :
I tried to draw those glitched images using IImageList.Draw() and it seem to work. So clearly the problem come from how I create an image from an HICON.
var hdc = GetDC(notepadHWnd);
var dp = new IMAGELISTDRAWPARAMS(
hdc,
new RECT(73, 73, 73, 73), 12,
COLORREF.None,
IMAGELISTDRAWFLAGS.ILD_NORMAL);
sHil.Interface.Draw(dp);
As I am stubborn I insisted with the IMAGELIST of the desktop listview.
I managed to get the icons / thumbnails from it. By drawing them in a in-memory device context.
No more glitched images.
private void Saving()
{
var hWnd = GetListViewHWnd(); // Desktop SysListView32 HWND
IntPtr lParam = IntPtr.Zero;
IntPtr pHil = SendMessage(hWnd, ListViewMessage.LVM_GETIMAGELIST, 0, ref lParam);
var sHil = new SafeHIMAGELIST(pHil); // IMAGELIST of the ListView
sHil.Interface.GetIconSize(out var cx, out var cy);
var imageCount = sHil.Interface.GetImageCount(); // IImageList Interface
var desktopHdc = new SafeHDC(GetDC(GetListViewHWnd()).DangerousGetHandle());
var inMemoryHdc = CreateCompatibleDC(desktopHdc);
for (int i = 0; i < imageCount; i++)
{
var inMemoryBmp = CreateCompatibleBitmap(desktopHdc, cx, cy);
SelectObject(inMemoryHdc, inMemoryBmp);
var ilDp = new IMAGELISTDRAWPARAMS(
inMemoryHdc,
new RECT(0, 0, 0, 0),
i,
COLORREF.None,
IMAGELISTDRAWFLAGS.ILD_NORMAL);
sHil.Interface.Draw(ilDp);
var bmpS = Imaging.CreateBitmapSourceFromHBitmap(
inMemoryBmp.DangerousGetHandle(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
using (var fs = File.OpenWrite(#"C:\Users\Julien\Desktop\Icons\" + i + ".png"))
{
BitmapEncoder enc = new PngBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bmpS));
enc.Save(fs);
}
inMemoryBmp.Dispose();
}
sHil.Dispose();
desktopHdc.Dispose();
inMemoryHdc.Dispose();
}
But as Jonathan Potter said it is not a good idea :
A combination of IShellItemImageFactory and SHCreateItemFromIDList for example seem better.
How to save the graph obtained after processing at avi file. Managed to get pictures with the overlay's text. I know that there is a method SetOutputFileName(), but how to use it here?
private Bitmap bitmapOverlay;
private IFilterGraph2 m_FilterGraph;
void GO()
{
SetupGraph("C:\\Export.avi");
SetupBitmap();
IMediaControl mediaCtrl = m_FilterGraph as IMediaControl;
int hr = mediaCtrl.Run();
DsError.ThrowExceptionForHR( hr );
}
private void SetupGraph(string FileName)
{
int hr;
IBaseFilter ibfRenderer = null;
ISampleGrabber sampGrabber = null;
IBaseFilter capFilter = null;
IPin iPinInFilter = null;
IPin iPinOutFilter = null;
IPin iPinInDest = null;
// Get the graphbuilder object
m_FilterGraph = new FilterGraph() as IFilterGraph2;
// Get the SampleGrabber interface
sampGrabber = new SampleGrabber() as ISampleGrabber;
// Add the video source
hr = m_FilterGraph.AddSourceFilter(FileName, "Ds.NET FileFilter", out capFilter);
DsError.ThrowExceptionForHR( hr );
// Hopefully this will be the video pin
IPin iPinOutSource = DsFindPin.ByDirection(capFilter, PinDirection.Output, 0);
IBaseFilter baseGrabFlt = sampGrabber as IBaseFilter;
ConfigureSampleGrabber(sampGrabber);
iPinInFilter = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Input, 0);
iPinOutFilter = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Output, 0);
// Add the frame grabber to the graph
hr = m_FilterGraph.AddFilter( baseGrabFlt, "Ds.NET Grabber" );
DsError.ThrowExceptionForHR( hr );
hr = m_FilterGraph.Connect(iPinOutSource, iPinInFilter);
DsError.ThrowExceptionForHR( hr );
// Get the default video renderer
ibfRenderer = (IBaseFilter) new VideoRendererDefault();
// Add it to the graph
hr = m_FilterGraph.AddFilter( ibfRenderer, "Ds.NET VideoRendererDefault" );
DsError.ThrowExceptionForHR( hr );
iPinInDest = DsFindPin.ByDirection(ibfRenderer, PinDirection.Input, 0);
// Connect the graph. Many other filters automatically get added here
hr = m_FilterGraph.Connect(iPinOutFilter, iPinInDest);
DsError.ThrowExceptionForHR( hr );
SaveSizeInfo(sampGrabber);
}
Process video - draw on each frame text.
cc.Save ("C: \\ Test \\ img" + m_Count + ".jpg") - so get shots with superimposed text.
How to make the processed video file saved in avi file?
int ISampleGrabberCB.BufferCB( double SampleTime, IntPtr pBuffer, int BufferLen )
{
Graphics g;
String s;
float sLeft;
float sTop;
SizeF d;
g = Graphics.FromImage(bitmapOverlay);
g.Clear(System.Drawing.Color.Transparent);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
// Prepare to put the specified string on the image
g.DrawRectangle(System.Drawing.Pens.Blue, 0, 0, m_videoWidth - 1, m_videoHeight - 1);
g.DrawRectangle(System.Drawing.Pens.Blue, 1, 1, m_videoWidth - 3, m_videoHeight - 3);
d = g.MeasureString(m_String, fontOverlay);
sLeft = (m_videoWidth - d.Width) / 2;
sTop = (m_videoHeight - d.Height ) / 2;
g.DrawString(m_String, fontOverlay, System.Drawing.Brushes.Red,
sLeft, sTop, System.Drawing.StringFormat.GenericTypographic);
g.Dispose();
Bitmap v;
v = new Bitmap(m_videoWidth, m_videoHeight, m_stride,
PixelFormat.Format32bppArgb, pBuffer);
v.RotateFlip(RotateFlipType.Rotate180FlipX);
g = Graphics.FromImage(v);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
// draw the overlay bitmap over the video's bitmap
g.DrawImage(bitmapOverlay, 0, 0, bitmapOverlay.Width, bitmapOverlay.Height);
Bitmap cc = new Bitmap(v);
cc.Save("C:\\Test\\img" + m_Count + ".jpg");
g.Dispose();
v.Dispose();
m_Count++;
return 0;
}
Typically it should look like:
[File reader] -> [AVI Demuxer] -> (video pin) -> [Video decoder] -> [Sample grabber] -> [Video encoder] -> [AVI Muxer] -> [File writer]
|-> (audio pin) ->|
AVI file is a media container so you need to demultiplex it to separate streams and at the end to multiplex (modified) streams back to AVI container. When you got video stream it (typically) contains encoded video. So to modify it you need to decode it and then after modification encode it back to the same format. You don't need to do anything about audio stream, just direct it from demuxer straight to muxer. [File writer] filter allows you to specify output file name.
I don't know what is "Ds.NET FileFilter" and how it can demux and then decode the video, but seems it can because you can see your modified picture. AVI Muxer is a standard MS filter, I just don't remember its name. You need to choose a video encoder. I'd recommend first to build a simple graph in GraphEditor that doesn't modify the picture but just read->demux->decod->encode->mux->write to verify you have everything you need and they work fine. Just try to play resulting AVI file.
For example on a label or in a textBox.
This is the code im trying now using DirectShowLib-2005.dll
private void button5_Click(object sender, EventArgs e)
{
f = new WmvAdapter(_videoFile);
TimeSpan ts = TimeSpan.FromTicks(f._duration);
MessageBox.Show(ts.ToString());
int t = 1;
const int WS_CHILD = 0x40000000;
const int WS_CLIPCHILDREN = 0x2000000;
_videoFile = Options_DB.get_loadedVideo();
FilgraphManager graphManager = new FilgraphManager();
graphManager.RenderFile(_videoFile);
videoWindow = (IVideoWindow)graphManager;
videoWindow.Owner = (int)pictureBox1.Handle;
videoWindow.WindowStyle = WS_CHILD | WS_CLIPCHILDREN;
videoWindow.SetWindowPosition(
pictureBox1.ClientRectangle.Left,
pictureBox1.ClientRectangle.Top,
pictureBox1.ClientRectangle.Width,
pictureBox1.ClientRectangle.Height);
mc = (IMediaControl)graphManager;
mc.Run();
When i click the button the file is playing and i see first the duration in the MessageBox.Show wich show me: 00:02:47.4800000
So first thing is that the duration is wrong since the file play length is: 00:04:36 when im looking on the file on the hard disk.
My goal is to show some progressBar or without a progressBar for now just on a label the time left for playing the video backwards. If the duration is 00:04:36 so i want to show it go back 00:04:35 ... 00:04:34 and so on.
The variable _duration is long and i tried to convert it to TimeSpan.
But the video length is not the same as it is when im looking on the file on the hard disk.
This is the functin wich i didn't create just using it from the class WmvAdapter:
private void SetupGraph(string file)
{
ISampleGrabber sampGrabber = null;
IBaseFilter capFilter = null;
IBaseFilter nullrenderer = null;
_filterGraph = (IFilterGraph2)new FilterGraph();
_mediaCtrl = (IMediaControl)_filterGraph;
_mediaEvent = (IMediaEvent)_filterGraph;
_mSeek = (IMediaSeeking)_filterGraph;
var mediaFilt = (IMediaFilter)_filterGraph;
try
{
// Add the video source
int hr = _filterGraph.AddSourceFilter(file, "Ds.NET FileFilter", out capFilter);
DsError.ThrowExceptionForHR(hr);
// Get the SampleGrabber interface
sampGrabber = new SampleGrabber() as ISampleGrabber;
var baseGrabFlt = sampGrabber as IBaseFilter;
ConfigureSampleGrabber(sampGrabber);
// Add the frame grabber to the graph
hr = _filterGraph.AddFilter(baseGrabFlt, "Ds.NET Grabber");
DsError.ThrowExceptionForHR(hr);
// ---------------------------------
// Connect the file filter to the sample grabber
// Hopefully this will be the video pin, we could check by reading it's mediatype
IPin iPinOut = DsFindPin.ByDirection(capFilter, PinDirection.Output, 0);
// Get the input pin from the sample grabber
IPin iPinIn = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Input, 0);
hr = _filterGraph.Connect(iPinOut, iPinIn);
DsError.ThrowExceptionForHR(hr);
// Add the null renderer to the graph
nullrenderer = new NullRenderer() as IBaseFilter;
hr = _filterGraph.AddFilter(nullrenderer, "Null renderer");
DsError.ThrowExceptionForHR(hr);
// ---------------------------------
// Connect the sample grabber to the null renderer
iPinOut = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Output, 0);
iPinIn = DsFindPin.ByDirection(nullrenderer, PinDirection.Input, 0);
hr = _filterGraph.Connect(iPinOut, iPinIn);
DsError.ThrowExceptionForHR(hr);
// Turn off the clock. This causes the frames to be sent
// thru the graph as fast as possible
hr = mediaFilt.SetSyncSource(null);
DsError.ThrowExceptionForHR(hr);
// Read and cache the image sizes
SaveSizeInfo(sampGrabber);
//Edit: get the duration
hr = _mSeek.GetDuration(out _duration);
DsError.ThrowExceptionForHR(hr);
}
finally
{
if (capFilter != null)
{
Marshal.ReleaseComObject(capFilter);
}
if (sampGrabber != null)
{
Marshal.ReleaseComObject(sampGrabber);
}
if (nullrenderer != null)
{
Marshal.ReleaseComObject(nullrenderer);
}
GC.Collect();
}
}
The duration before i converted it to TimeSpan was in the variable _duration: 1674800000
I tried a lot of examples and stuff but i couldn't get far from the TimeSpan convertion.
How can i do it please ?
Thank you.
This question seems related: Determine length of audio file using DirectShow
The answer there states:
GetDuration returns a 64bit integer value for how long it would take to play the file.
You will need to call the GetTimeFormat method to find out what units the duration is in. The most likely default value is TIME_FORMAT_MEDIA_TIME which is 10ths of a microsecond.
IN that case you would divide the duration by 10*1000*1000 to get seconds.
You can also call SetTimeFormat before calling GetDuration if you want to force the units.
So in your case, I'd use GetTimeFormat() to figure out the units and use that to convert it to the correct units for a TimeSpan object.
I try to use this code to get pictures of my cam:
IGraphBuilder _graph = null;
ISampleGrabber _grabber = null;
IBaseFilter _sourceObject = null;
IBaseFilter _grabberObject = null;
IMediaControl _control = null;
// Create the main graph
_graph = Activator.CreateInstance(Type.GetTypeFromCLSID(FilterGraph)) as IGraphBuilder;
// Create the webcam source
_sourceObject = FilterInfo.CreateFilter(_monikerString);
// Create the grabber
_grabber = Activator.CreateInstance(Type.GetTypeFromCLSID(SampleGrabber)) as ISampleGrabber;
_grabberObject = _grabber as IBaseFilter;
// Add the source and grabber to the main graph
_graph.AddFilter(_sourceObject, "source");
_graph.AddFilter(_grabberObject, "grabber");
IPin pin = _sourceObject.GetPin(PinDirection.Output, 0);
IAMStreamConfig streamConfig = pin as IAMStreamConfig;
int count = 0, size = 0;
streamConfig.GetNumberOfCapabilities(out count, out size);
int width = 0, height = 0;
AMMediaType mediaType = null;
AMMediaType mediaTypeCandidate = null;
for(int index = 0; index < count; index++) {
VideoStreamConfigCaps scc = new VideoStreamConfigCaps();
int test = streamConfig.GetStreamCaps(index, out mediaTypeCandidate, scc);
if(mediaTypeCandidate.MajorType == MediaTypes.Video && mediaTypeCandidate.SubType == MediaSubTypes.YUY2) {
VideoInfoHeader header = (VideoInfoHeader)Marshal.PtrToStructure(mediaTypeCandidate.FormatPtr, typeof(VideoInfoHeader));
if(header.BmiHeader.Width == 1280 && header.BmiHeader.Height == 720) {
width = header.BmiHeader.Width;
height = header.BmiHeader.Height;
if(mediaType != null)
mediaType.Dispose();
mediaType = mediaTypeCandidate;
} else
mediaTypeCandidate.Dispose();
} else
mediaTypeCandidate.Dispose();
}
streamConfig.SetFormat(mediaType);
And it works but i do not see the Image which is generated by this code:
uint pcount = (uint)(_capGrabber.Width * _capGrabber.Height * PixelFormats.Bgr32.BitsPerPixel / 8);
// Create a file mapping
_section = CreateFileMapping(new IntPtr(-1), IntPtr.Zero, 0x04, 0, pcount, null);
_map = MapViewOfFile(_section, 0xF001F, 0, 0, pcount);
// Get the bitmap
BitmapSource = Imaging.CreateBitmapSourceFromMemorySection(_section, _capGrabber.Width,
_capGrabber.Height, PixelFormats.Bgr32, _capGrabber.Width * PixelFormats.Bgr32.BitsPerPixel / 8, 0) as InteropBitmap;
_capGrabber.Map = _map;
// Invoke event
if (NewBitmapReady != null)
{
NewBitmapReady(this, null);
}
Because the SubMediaTyp is YUY2. How can i add a converter to this code? I have read something about a ColorConvert, which can be added to the IGraphBuilder. How does that work?
I would not expect CreateBitmapSourceFromMemorySection to accept anything else than flavors of RGB. Even more unlikely that it accepts YUY2 media type, so you need the DirectShow pipeline to convert video stream to RGB before you export it as a managed bitmap/imaging object.
To achieve this, you typically add Sample Grabber filter initialized to 24-bit RGB subtype and let DirectShow provide necessary converters automatically.
See detailed explanation and code snippets here: DirectShow: Examples for Using SampleGrabber for Grabbing a Frame and...
media.majorType = MediaType.Video;
media.subType = MediaSubType.RGB24;
media.formatPtr = IntPtr.Zero;
hr = sampGrabber.SetMediaType(media);
I'm using a SampleGrabber to get audio data, however my BufferCB method is not being executed. What am I doing wrong ?
//add Sample Grabber
IBaseFilter pSampleGrabber = (IBaseFilter)Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID_SampleGrabber));
hr = pGraph.AddFilter(pSampleGrabber, "SampleGrabber");
checkHR(hr, "Can't add Sample Grabber");
AMMediaType pSampleGrabber_pmt = new AMMediaType();
//pSampleGrabber_pmt.majorType = MediaType.Audio;
pSampleGrabber_pmt.subType = MediaSubType.PCM;
pSampleGrabber_pmt.formatType = FormatType.WaveEx;
pSampleGrabber_pmt.fixedSizeSamples = true;
pSampleGrabber_pmt.formatSize = 18;
pSampleGrabber_pmt.sampleSize = 2;
WaveFormatEx pSampleGrabber_Format = new WaveFormatEx();
pSampleGrabber_Format.wFormatTag = 1;
pSampleGrabber_Format.nChannels = 1;
pSampleGrabber_Format.nSamplesPerSec = 48000;
pSampleGrabber_Format.nAvgBytesPerSec = 96000;
pSampleGrabber_Format.nBlockAlign = 2;
pSampleGrabber_Format.wBitsPerSample = 16;
pSampleGrabber_pmt.formatPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(pSampleGrabber_Format));
Marshal.StructureToPtr(pSampleGrabber_Format, pSampleGrabber_pmt.formatPtr, false);
hr = ((ISampleGrabber)pSampleGrabber).SetMediaType(pSampleGrabber_pmt);
DsUtils.FreeAMMediaType(pSampleGrabber_pmt);
checkHR(hr, "Can't set media type to sample grabber");
ISampleGrabber pGrabber = new SampleGrabber() as ISampleGrabber;
pGrabber = (ISampleGrabber)pSampleGrabber;
pGrabber.SetCallback(null, 1);
My BufferCB method is like
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
{
return 0;
}
You created and configured one instance pSampleGrabber and then you are attaching your callback to another unused idling instance pGrabber.
You need
pSampleGrabber as ISampleGrabber
instead of
new SampleGrabber() as ISampleGrabber