Offscreen surface for video frame capture

Started by
1 comment, last by Headkaze 16 years ago
When I am copying a video frame into an offscreen surface most of the time it works. But when I try to copy strange video sizes such as 220 x 256 or 200 x 256 the video is distorted (offset). To show you want I mean here are some frameshots The first one shows a 220 x 256 video before adding a 16 bit (4 pixel) offset to each stride. Surface Before And after adding the 16 bit offset to each stride corrects the image. Surface After Both the source frame and destination offscreen surface are A8R8G8B8 so there is no problem there AFAIK. But it seems for some strange reason, an offscreen surface doesn't like some of the widths the video's are. Here is the code I'm using to create the offscreen surface and then copy the frame to it.
m_videoSurface = device.CreateOffscreenPlainSurface(m_videoWidth, m_videoHeight, Format.A8R8G8B8, Pool.Default);

pBuffer = Marshal.AllocCoTaskMem(BufferLen);

hr = m_sampGrabber.GetCurrentBuffer(ref BufferLen, pBuffer);
DsError.ThrowExceptionForHR(hr);

GraphicsStream gs = m_videoSurface.LockRectangle(LockFlags.Discard);

CopyMemory(gs.InternalData, pBuffer, (uint)BufferLen);

m_videoSurface.UnlockRectangle();

device.StretchRectangle(
m_videoSurface,
new Rectangle(0, 0, m_videoWidth, m_videoHeight),
surface,
new Rectangle(Point.Empty, m_frameSize),
TextureFilter.None
);

Now that works fine for video sizes 320 x 240, 256 x 192 etc. But here are some examples of what I need to do to correct some video sizes. For a 220 x 256 Video
int offset1 = 0;
int offset2 = 0;

for (int i = 0; i < m_videoHeight; i++)
{
	CopyMemory((IntPtr)(gs.InternalData.ToInt32() + offset2), (IntPtr)(pBuffer.ToInt32() + offset1), (uint)(m_stride));
	offset1 += m_stride;
	offset2 += m_stride + 16;
}

Notice the 16 bit (4 pixel) offset I have to add for each stride of the offscreen surface? Similar thing for a 200 x 256 video
for (int i = 0; i < m_videoHeight; i++)
{
	CopyMemory((IntPtr)(gs.InternalData.ToInt32() + offset2), (IntPtr)(pBuffer.ToInt32() + offset1), (uint)(m_stride));
	offset1 += m_stride;
	offset2 += m_stride + 32;
}

This time I need a 32 bit (8 pixel) offset for the image to not appear warped. So onto my questions - I assume the offscreen surface can't be strange sizes such as 220 or 200 pixels in width? So there is some sort of padding going on? - How do I calculate how much extra the surface needs for padding? - Can I avoid doing a CopyMemory for every stride of every video frame or is jumping the padding like this the only way? Obviously it's a performance hit having to do that. - And finally, is there a better way to do this perhaps? [Edited by - Headkaze on April 22, 2008 6:58:05 AM]
Advertisement
You need to work out the correct stride and do one CopyMemory() per scanline. Normally the stride is returned from IDirect3DSurface9::LockRect() but I'm not sure how you get at it in managed code.

Alternatively take a look at GetRenderTargetData() instead which will do it all for you.
Thanks heaps Adam, your a life saver!

Here is the solution for anyone else

// Allocate the buffer and read itpBuffer = Marshal.AllocCoTaskMem(BufferLen);hr = m_sampGrabber.GetCurrentBuffer(ref BufferLen, pBuffer);DsError.ThrowExceptionForHR(hr);int pitch = 0;GraphicsStream gs = m_videoSurface.LockRectangle(LockFlags.Discard, out pitch);if (pitch == m_stride)	CopyMemory(gs.InternalData, pBuffer, (uint)BufferLen);else	for (int i = 0; i < m_videoHeight; i++)		CopyMemory((IntPtr)(gs.InternalData.ToInt32() + i * pitch), (IntPtr)(pBuffer.ToInt32() + i * m_stride), (uint)m_stride);m_videoSurface.UnlockRectangle();

This topic is closed to new replies.

Advertisement