Closed genejo closed 4 years ago
I do not have USB camera that supports such large resolution, so I can not test your problem.
Judgeing from the image you posted, new image is overlapped on old image. As you mentioned, buffer copied from DirectShow is already teared, I think.
To get image from DirectShow, I use 'SampleGrabber'. And to get image from SampleGrabber, there are 2 ways. I don't know if it works or not, could you try 2nd way?
I'll post sample code later.
Add the code below in UsbCamera class.
private class SampleGrabberCallback : DirectShow.ISampleGrabberCB
{
private byte[] Buffer;
private object BufferLock = new object();
public Bitmap GetBitmap(int width, int height, int stride)
{
var result = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
if (Buffer == null) return result;
var bmp_data = result.LockBits(new Rectangle(Point.Empty, result.Size), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
lock (BufferLock)
{
// copy from last row.
for (int y = 0; y < height; y++)
{
var src_idx = Buffer.Length - (stride * (y + 1));
var dst = new IntPtr(bmp_data.Scan0.ToInt32() + (stride * y));
Marshal.Copy(Buffer, src_idx, dst, stride);
}
}
result.UnlockBits(bmp_data);
return result;
}
// called when each sample completed.
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
{
if (Buffer == null || Buffer.Length != BufferLen)
{
Buffer = new byte[BufferLen];
}
lock (BufferLock)
{
Marshal.Copy(pBuffer, Buffer, 0, BufferLen);
}
return 0;
}
// never called.
public int SampleCB(double SampleTime, DirectShow.IMediaSample pSample)
{
throw new NotImplementedException();
}
}
private Func<Bitmap> GetBitmapForSampleGrabberCallback(DirectShow.ISampleGrabber i_grabber, int width, int height, int stride)
{
var sampler = new SampleGrabberCallback();
i_grabber.SetCallback(sampler, 1); // WhichMethodToCallback = BufferCB
return () => sampler.GetBitmap(width, height, stride);
}
And replace the line below. (around line 161)
//GetBitmap = () => GetBitmapMain(i_grabber, width, height, stride);
GetBitmap = GetBitmapForSampleGrabberCallback(i_grabber, width, height, stride);
That is all. Could you let me know this works well or not.
Hey, thank you for the reply! Apologies for writing only now - turns out I was subscribed to notifications, but the e-mail notification checkbox was unchecked 🙄.
Thank you for sharing the other callback for the sample grabber! It does seem to work well (no tearing).
As a minor thing, it crashes after a couple of seconds of running at line
var result = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
with
System.ArgumentException: Parameter is not valid.
even though width
and height
seem appropriate, but it doesn't matter. I'm happy with the rgb24 byte frame anyway.
This is really a neat self-contained 1 file library, @secile !
Thank you for your reply. I'm glad that using callback works fine.
1st way use sample grabber's GetCurrentBuffer function, and according to the page below, the function can be cause screen tearing problem.
https://docs.microsoft.com/en-us/windows/win32/directshow/isamplegrabber-getcurrentbuffer
Do not call this method while the filter graph is running. While the filter graph is running, the Sample Grabber filter overwrites the contents of the buffer whenever it receives a new sample.
So, using callbacks seems to be the right way. I will fix future release.
One thing worry about is you reported that app crashed after a couple of seconds of running. On my PC, running for 3 hours did not cause any problems. Is this problem still happening?
It only happens for 3840 x 2160 and 2592 x 1944. The version using the grabber's function doesn't crash... but aside from tearing it's also a bit more dragging.
With the callback, after the first exception is thrown all future bitmap allocation attempts throw the same exception. I'm planning to investigate at some point and come back if things become clear.
Do you happen to know if anything can be done to avoid screen tearing while capturing at higher resolutions?
For instance, if I capture at 2592 x 1944 (~5 MP), I get this kind of artifacts:
Seemingly there's not much that can be done within the class (method
GetBitmapMainMain()
). This is how the localdata
buffer is copied from the unmanaged DirectShow buffer - already teared. Any settings for DirectShow maybe, or is there not much hope?