JohnWein

In a video player, I am trying to get the current image from the Video Mixing Renderer 9. I use the "DirectShowLib" from SourceForge to get the image data since GetCurrentImage isn't supported in the "QuartzTypeLib". I would then like to use the data to make a Bitmap. I tried to use the Scan0 ptr form of new Bitmap but I can't get it to work. I get a generic GDI+ error.

Can someone tell me how to call the GetCurrentImage function in "Quartz.dll" directly

Can someone tell me how to get the data returned from "GetCurrentImage" directly into a Bitmap

I don't want to copy the data or save the data to disk. I need speed.

Here's my code:

Code Snippet

Imports DirectShowLib

Imports System.Runtime.InteropServices

Public Class Form1

Private FileName As String

Private FGM As IGraphBuilder

Private MC As IMediaControl

Private Vid As IBasicVideo2

Private Buf() As Int32

Private pBuf As IntPtr

Private gchBuf As GCHandle

Private Bmp As BITMAP

Private Sub RenderFile()

FGM = New FilterGraph

Dim VMR9 As IBaseFilter = New VideoMixingRenderer9

FGM.AddFilter(VMR9, "Video Mixing Renderer 9")

FGM.RenderFile(FileName, 0)

MC = FGM

MC.Run()

Vid = FGM

End Sub

Private Sub OpenFileToolStripMenuItem_Click _

(ByVal sender As System.Object, _

ByVal e As System.EventArgs) _

Handles OpenFileToolStripMenuItem.Click

If OpenFileDialog1.ShowDialog() = _

Windows.Forms.DialogResult.OK Then

FileName = OpenFileDialog1.FileName

RenderFile()

End If

End Sub

Private Sub CaptureFrameToolStripMenuItem_Click _

(ByVal sender As System.Object, _

ByVal e As System.EventArgs) _

Handles CaptureFrameToolStripMenuItem.Click

If MC Is Nothing Then Return

MC.Pause()

Dim BufSize As Int32

Vid.GetCurrentImage(BufSize, 0)

Dim UbBuf As Int32 = (BufSize >> 2) - 1

ReDim Buf(UbBuf)

gchBuf = GCHandle.Alloc(Buf, GCHandleType.Pinned)

pBuf = gchBuf.AddrOfPinnedObject

Vid.GetCurrentImage(BufSize, pBuf)

'Start of code

Dim pScan0 As IntPtr = CType(CInt(pBuf) + 40, IntPtr)

Bmp = New Bitmap(Buf(1), Buf(2), -4 * Buf(1), _

Imaging.PixelFormat.Format32bppRgb, pScan0)

Bmp.Save("C:\Test.bmp", Imaging.ImageFormat.Bmp)

'End of code

End Sub

Private Sub ShowFrameToolStripMenuItem_Click _

(ByVal sender As System.Object, _

ByVal e As System.EventArgs) _

Handles ShowFrameToolStripMenuItem.Click

With PictureBox1

.Image = New Bitmap("C:\Test.bmp")

.Width = .Image.Width

.Height = .Image.Height

Width = .Width + 8

Height = .Height + 54

End With

End Sub

End Class

It's the lines between 'Start of code and 'End Of code that I need help with.

If I replace those lines with the following I can verify that the data in the array are correct.

Code Snippet

Dim Bmfh As New Win.BITMAPFILEHEADER

Bmfh.bfType = &H4D42

Bmfh.bfSize = BufSize + 14

Bmfh.bfOffBits = 54

FileOpen(1, "C:\Test.bmp", OpenMode.Binary)

FilePut(1, Bmfh)

FilePut(1, Buf)

FileClose(1)

gchBuf.Free()



Re: Visual Basic Language Unmanaged array to Bitmap?

jo0ls

I don't know about quartz.dll

This works:


Code Snippet

Dim pScan0 As IntPtr = CType(CInt(pBuf) + 40, IntPtr)

Dim wid As Integer

Dim hei As Integer

Vid.GetVideoSize(wid, hei)

Dim Bmp As New Bitmap(wid, hei, wid * 4, _

Imaging.PixelFormat.Format32bppArgb, pScan0)

gchBuf.Free()

Bmp.RotateFlip(RotateFlipType.RotateNoneFlipY)

Bmp.Save(My.Computer.FileSystem.SpecialDirectories.MyDocuments & "\test.bmp", Imaging.ImageFormat.Bmp)













Re: Visual Basic Language Unmanaged array to Bitmap?

JohnWein

Amazing! The Stride is wrong. How did you figure that out so quickly

Code Snippet

Bmp = New Bitmap(Buf(1), Buf(2), 4 * Buf(1), _

Imaging.PixelFormat.Format32bppRgb, pScan0)

Bmp.RotateFlip(RotateFlipType.RotateNoneFlipY)

The only change necessary was to remove the "-" sign on the stride and flip the Bitmap. Works great!





Re: Visual Basic Language Unmanaged array to Bitmap?

jo0ls

I translated c.graus' image processing filters to vb.net and learnt all about lockbits, image formats and strides in the process. I didn't understand what all the buf(x) references were and just skipped ahead to using the width, height and assumed it was 4 bytes per pixel. (I see now that it comes with a bitmapInfoHeader. I looked into using api calls to covert the dib into a bitmap but it sounds like there is no benefit to doing that.

The stride represents the width of 1 row of pixels in bytes in memory. So, it can't be negative. There is a problem if there are 3 bytes per pixel as it gets padded to the nearest 4 byte boundary, but if the videos are always 4 bytes per pixel then you'll be ok.

One thing I've just learned, when you create a bitmap using an intptr to the scan0 of an image - the new bitmap accesses that memory directly - so no copying takes place. This means that you need to keep it pinned for the duration of the bitmap. So don't free the handle until after you have saved it. (I doubt the garbage collector would be able to get it that quickly though).

You are probably going to want to display the image - which means you will need to either keep it's memory pinned or copy it before assigning the copy to the picturebox.image property and freeing the handle.





Re: Visual Basic Language Unmanaged array to Bitmap?

JohnWein

I got the impression that a top down Bitmap had a positive stride and a bottom up Bitmap had a negative stride. I checked the stride and scan0 before and after the flip. The stride stays positive, but the scan0 changes. Since the data is moved in memory, I can probably free the pinned memory.

I have a number of reasons for getting the image from the filtergraph. I am building a video player/editor for high definition TV video. I want to analyse the video to see if it's 16x9 or 4x3. If it's 4x3 I want to find where to crop it. I also want to store the video frame by frame to analyze golf swings. And, since the video is mpeg 2, I want to display each frame for precise editing.

You certainly learned a lot by translating Christian Graus' articles. Could I get a copy of your code

I'm glad that we can't use pointers in VB, If we could we'd probably have to contend with "unsafe code" restrictions.