在 DirectShow 中预览相机并捕获静止图像 - 在 VB.net 中
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19465932/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Preview a camera in DirectShow and capture a still image - in VB.net
提问by Jeremy Kropf
I am trying to write a program in Visual Studio 2008 that will access a webcam, show a preview on the screen and then save a still snapshot (.jpg) when the button is pushed. Later I am going to integrate it with a database, but I shouldn't have a problem with that part. After doing some research, it looks like DirectShow is the best bet, because WIA didn't work on the camera I had (and I'm not sure it will continue to work into the future). Preferably, I need my solution to work from Windows XP to Windows 7. I have never used DirectShow (or similar) before. One problem I am running into is that most of the code is written in C#, which I have never learned. I found a DirectShow.Net library that also uses vb.net, so that is helpful, but I am still having problems. The following code is taken from the samples in the library, and works, but I want to alter it somewhat and can't quite get it to work. The code right now saves the camera capture to a file. I can rem out the "capGraph.SetOutputFileName" line, the video will just launch into its own window, but I don't how to control that. Basically, I would like to know how to do two things:
我正在尝试在 Visual Studio 2008 中编写一个程序,该程序将访问网络摄像头,在屏幕上显示预览,然后在按下按钮时保存静态快照 (.jpg)。稍后我打算将它与数据库集成,但那部分应该没有问题。经过一些研究,看起来 DirectShow 是最好的选择,因为 WIA 无法在我拥有的相机上工作(而且我不确定它是否会在未来继续工作)。最好,我需要我的解决方案从 Windows XP 工作到 Windows 7。我以前从未使用过 DirectShow(或类似的)。我遇到的一个问题是,大部分代码都是用 C# 编写的,我从未学过。我找到了一个也使用 vb.net 的 DirectShow.Net 库,这很有帮助,但我仍然遇到问题。以下代码取自库中的示例,并且有效,但我想稍微改变它并且不能让它工作。代码现在将相机捕获保存到文件中。我可以删除“capGraph.SetOutputFileName”行,视频将直接启动到它自己的窗口中,但我不知道如何控制它。基本上,我想知道如何做两件事:
- How do I get DirectShow to display in a control on a form that I specify (picturebox?)?
- Can I then get a snapshot of that video when the user clicks a button (it can pause the video, or whatever, because at that point I don't need the preview to resume, at least not for a number of seconds.)
- 如何让 DirectShow 显示在我指定的窗体上的控件中(图片框?)?
- 然后我可以在用户单击按钮时获取该视频的快照吗(它可以暂停视频,或者其他什么,因为那时我不需要恢复预览,至少不需要几秒钟。)
Thanks a lot, and sorry if some of this is not phrased very well. I am self-taught, and have done a lot in vba and php, but this is a little beyond my experience.
非常感谢,如果其中一些措辞不是很好,很抱歉。我是自学的,在 vba 和 php 方面做了很多,但这有点超出我的经验。
'****************************************************************************
'While the underlying libraries are covered by LGPL, this sample is released
'as public domain. It is distributed in the hope that it will be useful, but
'WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
'or FITNESS FOR A PARTICULAR PURPOSE.
'*****************************************************************************/
Imports System
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
Imports System.Diagnostics
Imports DirectShowLib
Public Class Capture
Implements ISampleGrabberCB
Implements IDisposable
#Region "Member variables"
' <summary> graph builder interface. </summary>
Private m_graphBuilder As IFilterGraph2 = Nothing
Private m_mediaCtrl As IMediaControl = Nothing
' <summary> Set by async routine when it captures an image </summary>
Private m_bRunning As Boolean = False
' <summary> Dimensions of the image, calculated once in constructor. </summary>
Private m_videoWidth As Integer
Private m_videoHeight As Integer
Private m_stride As Integer
Private m_bmdLogo As BitmapData = Nothing
Private m_Bitmap As Bitmap = Nothing
#If Debug Then
' Allow you to "Connect to remote graph" from GraphEdit
Private m_rot As DsROTEntry = Nothing
#End If
#End Region
#Region "API"
Declare Sub CopyMemory Lib "Kernel32.dll" Alias "RtlMoveMemory" (ByVal Destination As IntPtr, ByVal Source As IntPtr, <MarshalAs(UnmanagedType.U4)> ByVal Length As Integer)
#End Region
' zero based device index, and some device parms, plus the file name to save to
Public Sub New(ByVal iDeviceNum As Integer, ByVal iFrameRate As Integer, ByVal iWidth As Integer, ByVal iHeight As Integer, ByVal FileName As String)
Dim capDevices As DsDevice()
' Get the collection of video devices
capDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice)
If (iDeviceNum + 1 > capDevices.Length) Then
Throw New Exception("No video capture devices found at that index!")
End If
Dim dev As DsDevice = capDevices(iDeviceNum)
Try
' Set up the capture graph
SetupGraph(dev, iFrameRate, iWidth, iHeight, FileName)
Catch
Dispose()
Throw
End Try
End Sub
' <summary> release everything. </summary>
Public Sub Dispose() Implements IDisposable.Dispose
CloseInterfaces()
If (Not m_Bitmap Is Nothing) Then
m_Bitmap.UnlockBits(m_bmdLogo)
m_Bitmap = Nothing
m_bmdLogo = Nothing
End If
End Sub
Protected Overloads Overrides Sub finalize()
CloseInterfaces()
End Sub
' <summary> capture the next image </summary>
Public Sub Start()
If (m_bRunning = False) Then
Dim hr As Integer = m_mediaCtrl.Run()
DsError.ThrowExceptionForHR(hr)
m_bRunning = True
End If
End Sub
' Pause the capture graph.
' Running the graph takes up a lot of resources. Pause it when it
' isn't needed.
Public Sub Pause()
If (m_bRunning) Then
Dim hr As Integer = m_mediaCtrl.Pause()
DsError.ThrowExceptionForHR(hr)
m_bRunning = False
End If
End Sub
' <summary> Specify the logo file to write onto each frame </summary>
Public Sub SetLogo(ByVal fileName As String)
SyncLock Me
If (fileName.Length > 0) Then
m_Bitmap = New Bitmap(fileName)
Dim r As Rectangle = New Rectangle(0, 0, m_Bitmap.Width, m_Bitmap.Height)
m_bmdLogo = m_Bitmap.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb)
Else
If Not m_Bitmap Is Nothing Then
m_Bitmap.UnlockBits(m_bmdLogo)
m_Bitmap = Nothing
m_bmdLogo = Nothing
End If
End If
End SyncLock
End Sub
' <summary> build the capture graph for grabber. </summary>
Private Sub SetupGraph(ByVal dev As DsDevice, ByVal iFrameRate As Integer, ByVal iWidth As Integer, ByVal iHeight As Integer, ByVal FileName As String)
Dim hr As Integer
Dim sampGrabber As ISampleGrabber = Nothing
Dim baseGrabFlt As IBaseFilter = Nothing
Dim capFilter As IBaseFilter = Nothing
Dim muxFilter As IBaseFilter = Nothing
Dim fileWriterFilter As IFileSinkFilter = Nothing
Dim capGraph As ICaptureGraphBuilder2 = Nothing
' Get the graphbuilder object
m_graphBuilder = DirectCast(New FilterGraph(), IFilterGraph2)
m_mediaCtrl = DirectCast(m_graphBuilder, IMediaControl)
#If Debug Then
m_rot = New DsROTEntry(m_graphBuilder)
#End If
Try
' Get the ICaptureGraphBuilder2
capGraph = DirectCast(New CaptureGraphBuilder2(), ICaptureGraphBuilder2)
' Get the SampleGrabber interface
sampGrabber = DirectCast(New SampleGrabber(), ISampleGrabber)
' Start building the graph
hr = capGraph.SetFiltergraph(DirectCast(m_graphBuilder, IGraphBuilder))
DsError.ThrowExceptionForHR(hr)
' Add the video device
hr = m_graphBuilder.AddSourceFilterForMoniker(dev.Mon, Nothing, dev.Name, capFilter)
DsError.ThrowExceptionForHR(hr)
baseGrabFlt = DirectCast(sampGrabber, IBaseFilter)
ConfigureSampleGrabber(sampGrabber)
' Add the frame grabber to the graph
hr = m_graphBuilder.AddFilter(baseGrabFlt, "Ds.NET Grabber")
DsError.ThrowExceptionForHR(hr)
' If any of the default config items are set
If (iFrameRate + iHeight + iWidth > 0) Then
SetConfigParms(capGraph, capFilter, iFrameRate, iWidth, iHeight)
End If
hr = capGraph.SetOutputFileName(MediaSubType.Avi, FileName, muxFilter, fileWriterFilter)
DsError.ThrowExceptionForHR(hr)
hr = capGraph.RenderStream(PinCategory.Capture, MediaType.Video, capFilter, baseGrabFlt, muxFilter)
DsError.ThrowExceptionForHR(hr)
SaveSizeInfo(sampGrabber)
Finally
If (Not fileWriterFilter Is Nothing) Then
Marshal.ReleaseComObject(fileWriterFilter)
fileWriterFilter = Nothing
End If
If (Not muxFilter Is Nothing) Then
Marshal.ReleaseComObject(muxFilter)
muxFilter = Nothing
End If
If (Not capFilter Is Nothing) Then
Marshal.ReleaseComObject(capFilter)
capFilter = Nothing
End If
If (Not sampGrabber Is Nothing) Then
Marshal.ReleaseComObject(sampGrabber)
sampGrabber = Nothing
End If
End Try
End Sub
' <summary> Read and store the properties </summary>
Private Sub SaveSizeInfo(ByVal sampGrabber As ISampleGrabber)
Dim hr As Integer
' Get the media type from the SampleGrabber
Dim media As AMMediaType = New AMMediaType()
hr = sampGrabber.GetConnectedMediaType(media)
DsError.ThrowExceptionForHR(hr)
If (Not (media.formatType.Equals(FormatType.VideoInfo)) AndAlso Not (media.formatPtr.Equals(IntPtr.Zero))) Then
Throw New NotSupportedException("Unknown Grabber Media Format")
End If
' Grab the size info
Dim vInfoHeader As VideoInfoHeader = New VideoInfoHeader()
Marshal.PtrToStructure(media.formatPtr, vInfoHeader)
m_videoWidth = vInfoHeader.BmiHeader.Width
m_videoHeight = vInfoHeader.BmiHeader.Height
m_stride = m_videoWidth * (vInfoHeader.BmiHeader.BitCount / 8)
DsUtils.FreeAMMediaType(media)
media = Nothing
End Sub
' <summary> Set the options on the sample grabber </summary>
Private Sub ConfigureSampleGrabber(ByVal sampGrabber As ISampleGrabber)
Dim hr As Integer
Dim media As AMMediaType = New AMMediaType()
media.majorType = MediaType.Video
media.subType = MediaSubType.RGB24
media.formatType = FormatType.VideoInfo
hr = sampGrabber.SetMediaType(media)
DsError.ThrowExceptionForHR(hr)
DsUtils.FreeAMMediaType(media)
media = Nothing
' Configure the samplegrabber callback
hr = sampGrabber.SetCallback(Me, 0)
DsError.ThrowExceptionForHR(hr)
End Sub
' Set the Framerate, and video size
Private Sub SetConfigParms(ByVal capGraph As ICaptureGraphBuilder2, ByVal capFilter As IBaseFilter, ByVal iFrameRate As Integer, ByVal iWidth As Integer, ByVal iHeight As Integer)
Dim hr As Integer
Dim o As Object = Nothing
Dim media As AMMediaType = Nothing
Dim videoStreamConfig As IAMStreamConfig
Dim videoControl As IAMVideoControl = DirectCast(capFilter, IAMVideoControl)
' Find the stream config interface
hr = capGraph.FindInterface(PinCategory.Capture, MediaType.Video, capFilter, GetType(IAMStreamConfig).GUID, o)
videoStreamConfig = DirectCast(o, IAMStreamConfig)
Try
If (videoStreamConfig Is Nothing) Then
Throw New Exception("Failed to get IAMStreamConfig")
End If
' Get the existing format block
hr = videoStreamConfig.GetFormat(media)
DsError.ThrowExceptionForHR(hr)
' copy out the videoinfoheader
Dim v As VideoInfoHeader = New VideoInfoHeader()
Marshal.PtrToStructure(media.formatPtr, v)
' if overriding the framerate, set the frame rate
If (iFrameRate > 0) Then
v.AvgTimePerFrame = 10000000 / iFrameRate
End If
' if overriding the width, set the width
If (iWidth > 0) Then
v.BmiHeader.Width = iWidth
End If
' if overriding the Height, set the Height
If (iHeight > 0) Then
v.BmiHeader.Height = iHeight
End If
' Copy the media structure back
Marshal.StructureToPtr(v, media.formatPtr, False)
' Set the new format
hr = videoStreamConfig.SetFormat(media)
DsError.ThrowExceptionForHR(hr)
DsUtils.FreeAMMediaType(media)
media = Nothing
' Fix upsidedown video
If (Not videoControl Is Nothing) Then
Dim pCapsFlags As VideoControlFlags
Dim pPin As IPin = DsFindPin.ByCategory(capFilter, PinCategory.Capture, 0)
hr = videoControl.GetCaps(pPin, pCapsFlags)
DsError.ThrowExceptionForHR(hr)
If ((pCapsFlags & VideoControlFlags.FlipVertical) > 0) Then
hr = videoControl.GetMode(pPin, pCapsFlags)
DsError.ThrowExceptionForHR(hr)
hr = videoControl.SetMode(pPin, 0)
End If
End If
Finally
Marshal.ReleaseComObject(videoStreamConfig)
End Try
End Sub
' <summary> Shut down capture </summary>
Private Sub CloseInterfaces()
Dim hr As Integer
Try
If (Not m_mediaCtrl Is Nothing) Then
' Stop the graph
hr = m_mediaCtrl.Stop()
m_mediaCtrl = Nothing
m_bRunning = False
End If
Catch ex As Exception
Debug.WriteLine(ex)
End Try
#If Debug Then
If (Not m_rot Is Nothing) Then
m_rot.Dispose()
m_rot = Nothing
End If
#End If
If (Not m_graphBuilder Is Nothing) Then
Marshal.ReleaseComObject(m_graphBuilder)
m_graphBuilder = Nothing
End If
GC.Collect()
End Sub
' <summary> sample callback, Originally not used - call this with integer 0 on the setcallback method </summary>
Function SampleCB(ByVal SampleTime As Double, ByVal pSample As IMediaSample) As Integer Implements ISampleGrabberCB.SampleCB
myTest = "In SampleCB"
Dim i As Integer=0
Dim hr As Integer
'jk added this code 10-22-13
if IsDBNull(pSample) =True then return -1
dim myLen As Integer = pSample.GetActualDataLength()
dim pbuf As IntPtr
if pSample.GetPointer(pbuf) = 0 AND mylen > 0 then
dim buf As byte()= new byte(myLen) {}
Marshal.Copy(pbuf, buf, 0, myLen)
for i = 0 to myLen-1 step 2
buf(i) = (255 - buf(i))
Next i
Dim g_RowSizeBytes As Integer
Dim g_PixBytes() As Byte
Dim bm As Bitmap = Nothing
Dim m_BitmapData As BitmapData = Nothing
Dim bounds As Rectangle = New Rectangle(0, 0, m_videoWidth, m_videoHeight)
mytest = "Execution point #2"
m_BitmapData = bm.LockBits(bounds, Imaging.ImageLockMode.ReadWrite, Imaging.PixelFormat.Format24bppRgb)
mytest = "Execution point #4"
g_RowSizeBytes = m_BitmapData.Stride
mytest = "Execution point #5"
' Allocate room for the data.
Dim total_size As Integer = m_BitmapData.Stride * m_BitmapData.Height
ReDim g_PixBytes(total_size)
mytest = "Execution point #10"
'this writes the modified data
Marshal.Copy(buf, 0, m_BitmapData.Scan0, mylen)
' Unlock the bitmap.
bm.UnlockBits(m_BitmapData)
' Release resources.
g_PixBytes = Nothing
m_BitmapData = Nothing
End If
Marshal.ReleaseComObject(pSample)
Return 0
End Function
' <summary> buffer callback, COULD BE FROM FOREIGN THREAD. </summary>
Function BufferCB(ByVal SampleTime As Double, ByVal pBuffer As IntPtr, ByVal BufferLen As Integer) As Integer Implements ISampleGrabberCB.BufferCB
SyncLock Me
If (Not m_bmdLogo Is Nothing) Then
Dim ipSource As IntPtr = m_bmdLogo.Scan0
Dim ipDest As IntPtr = pBuffer
Dim x As Integer
For x = 0 To m_bmdLogo.Height - 1
CopyMemory(ipDest, ipSource, m_bmdLogo.Stride)
ipDest = New IntPtr(ipDest.ToInt32() + m_stride)
ipSource = New IntPtr(ipSource.ToInt32() + m_bmdLogo.Stride)
Next x
End If
End SyncLock
Return 0
End Function
End Class
采纳答案by Jeremy Kropf
Here is what I finally used for my project - it gives you a preview window, and then on another form you can push a button to take the picture. I take the picture and display that stillshot image as another picturebox on the main form (a little larger). I added a cropbox to choose what portion of that picture box you would ultimately like to save, but am not including that in this answer for simplicities sake.
这是我最终在我的项目中使用的 - 它为您提供了一个预览窗口,然后在另一个表单上您可以按下按钮来拍照。我拍摄照片并将该静止图像显示为主窗体上的另一个图片框(稍大一点)。我添加了一个裁剪框来选择您最终想要保存的图片框的哪一部分,但为了简单起见,我没有将其包含在这个答案中。
You initialize it with the call: cam = New Capture(my.Settings.VideoDeviceNum, FRAMERATE, my.Settings.CapturePictureWidth, my.Settings.CapturePictureHeight)
and cam.Start()
你用调用初始化它:cam = New Capture(my.Settings.VideoDeviceNum, FRAMERATE, my.Settings.CapturePictureWidth, my.Settings.CapturePictureHeight)
和cam.Start()
The snapshot is take with this call:
使用此调用拍摄快照:
Dim picError As Boolean=False
cam.captureSaved=False
cam.TakePicture
Dim stTime As date = Now
Do Until cam.captureSaved
'do nothing - might want to have an automatic break if this takes too long
If DateDiff(DateInterval.Second,stTime,Now) >15 then
MsgBox("The camera is taking a long time to capture the picture. Please try again.")
picError=True:Exit do
End If
Loop
If not picError then
cam.Capturedpic.RotateFlip (RotateFlipType.Rotate180FlipX)
'scale the camera image and place it in the picture box
CaptureBox.Image=scaleimage(cam.capturedpic,CaptureBox.Width,CaptureBox.Height)
End If
SavePicture.Visible = True
myStatus.Text = "Picture Taken."
The scaleimage function simply scales the snapshot image to the appropriate size for the box I have on the form. I only work with scaling the x, because I only allow a certain aspect ratio, so if you aren't going to lock in your aspect ratios for the camera, it will need to be adjusted:
scaleimage 函数只是将快照图像缩放到我在表单上的框的适当大小。我只使用缩放 x,因为我只允许特定的纵横比,因此如果您不打算锁定相机的纵横比,则需要对其进行调整:
Public Function ScaleImage(source As Bitmap, x As Integer, y As Integer) As Bitmap
Dim scale As single = x / source.Width
Dim myBmp As new Bitmap(cint(source.Width*scale), cint(source.height*scale),source.PixelFormat)
Dim gr As Graphics = Graphics.FromImage(myBmp)
gr.DrawImage(source, 0, 0, myBmp.Width + 1, myBmp.Height + 1)
Return myBmp
End Function
The main camera class is below:
主要的相机类如下:
'****************************************************************************
'While the underlying libraries are covered by LGPL, this sample is released
'as public domain. It is distributed in the hope that it will be useful, but
'WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
'or FITNESS FOR A PARTICULAR PURPOSE.
'*****************************************************************************/
Imports System
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
Imports System.Diagnostics
Imports DirectShowLib
Public Class Capture
Implements ISampleGrabberCB
Implements IDisposable
#Region "Member variables"
' <summary> graph builder interface. </summary>
Private m_graphBuilder As IFilterGraph2 = Nothing
Private m_mediaCtrl As IMediaControl = Nothing
Private mediaEventEx As IMediaEventEx = Nothing
Private videoWindow As IVideoWindow = Nothing
Private UseHand As IntPtr = MainForm.PreviewBox.Handle
Private Const WMGraphNotify As Integer = 13
Private m_takePicture As Boolean = False
Public mytest As String = "yes"
Dim sampGrabber As ISampleGrabber = Nothing
Private bufferedSize As Integer = 0
Private savedArray() As Byte
Public capturedPic as bitmap
Public captureSaved As Boolean
Public unsupportedVideo As Boolean
' <summary> Set by async routine when it captures an image </summary>
Public m_bRunning As Boolean = False
' <summary> Dimensions of the image, calculated once in constructor. </summary>
Private m_videoWidth As Integer
Private m_videoHeight As Integer
Private m_stride As Integer
Private m_bmdLogo As BitmapData = Nothing
Private m_Bitmap As Bitmap = Nothing
#If Debug Then
' Allow you to "Connect to remote graph" from GraphEdit
Private m_rot As DsROTEntry = Nothing
#End If
#End Region
#Region "API"
Declare Sub CopyMemory Lib "Kernel32.dll" Alias "RtlMoveMemory" (ByVal Destination As IntPtr, ByVal Source As IntPtr, <MarshalAs(UnmanagedType.U4)> ByVal Length As Integer)
#End Region
' zero based device index, and some device parms, plus the file name to save to
Public Sub New(ByVal iDeviceNum As Integer, ByVal iFrameRate As Integer, ByVal iWidth As Integer, ByVal iHeight As Integer)
Dim capDevices As DsDevice()
' Get the collection of video devices
capDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice)
If (iDeviceNum + 1 > capDevices.Length) Then
Throw New Exception("No video capture devices found at that index!")
End If
Dim dev As DsDevice = capDevices(iDeviceNum)
Try
' Set up the capture graph
SetupGraph(dev, iFrameRate, iWidth, iHeight)
Catch
Dispose()
If unsupportedVideo then
msgbox("This video resolution isn't supported by the camera - please choose a different resolution.")
Else
Throw
End If
End Try
End Sub
' <summary> release everything. </summary>
Public Sub Dispose() Implements IDisposable.Dispose
CloseInterfaces()
If (Not m_Bitmap Is Nothing) Then
m_Bitmap.UnlockBits(m_bmdLogo)
m_Bitmap = Nothing
m_bmdLogo = Nothing
End If
End Sub
Protected Overloads Overrides Sub finalize()
CloseInterfaces()
End Sub
' <summary> capture the next image </summary>
Public Sub Start()
If (m_bRunning = False) Then
Dim hr As Integer = m_mediaCtrl.Run()
DsError.ThrowExceptionForHR(hr)
m_bRunning = True
End If
End Sub
' Pause the capture graph.
' Running the graph takes up a lot of resources. Pause it when it
' isn't needed.
Public Sub Pause()
If (m_bRunning) Then
Dim hr As Integer = m_mediaCtrl.Pause()
DsError.ThrowExceptionForHR(hr)
m_bRunning = False
End If
End Sub
'Added by jk
Public Sub TakePicture()
m_takePicture=True
End Sub
' <summary> Specify the logo file to write onto each frame </summary>
Public Sub SetLogo(ByVal fileName As String)
SyncLock Me
If (fileName.Length > 0) Then
m_Bitmap = New Bitmap(fileName)
Dim r As Rectangle = New Rectangle(0, 0, m_Bitmap.Width, m_Bitmap.Height)
m_bmdLogo = m_Bitmap.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb)
Else
If Not m_Bitmap Is Nothing Then
m_Bitmap.UnlockBits(m_bmdLogo)
m_Bitmap = Nothing
m_bmdLogo = Nothing
End If
End If
End SyncLock
End Sub
' <summary> build the capture graph for grabber. </summary>
Private Sub SetupGraph(ByVal dev As DsDevice, ByVal iFrameRate As Integer, ByVal iWidth As Integer, ByVal iHeight As Integer)
Dim hr As Integer
Dim baseGrabFlt As IBaseFilter = Nothing
Dim capFilter As IBaseFilter = Nothing
Dim muxFilter As IBaseFilter = Nothing
Dim fileWriterFilter As IFileSinkFilter = Nothing
Dim capGraph As ICaptureGraphBuilder2 = Nothing
Dim sampGrabberSnap As ISampleGrabber = Nothing
' Get the graphbuilder object
m_graphBuilder = DirectCast(New FilterGraph(), IFilterGraph2)
m_mediaCtrl = DirectCast(m_graphBuilder, IMediaControl)
'if taking a picture (a still snapshot), then remove the videowindow
If not m_takePicture then
mediaEventEx = DirectCast(m_graphBuilder, IMediaEventEx)
videoWindow = DirectCast(m_graphBuilder, IVideoWindow)
Else
mediaEventEx = Nothing
videoWindow = Nothing
End If
#If Debug Then
m_rot = New DsROTEntry(m_graphBuilder)
#End If
Try
' Get the ICaptureGraphBuilder2
capGraph = DirectCast(New CaptureGraphBuilder2(), ICaptureGraphBuilder2)
' Get the SampleGrabber interface
sampGrabber = DirectCast(New SampleGrabber(), ISampleGrabber)
sampGrabberSnap = DirectCast(New SampleGrabber(), ISampleGrabber)
' Start building the graph
hr = capGraph.SetFiltergraph(DirectCast(m_graphBuilder, IGraphBuilder))
DsError.ThrowExceptionForHR(hr)
' Add the video device
hr = m_graphBuilder.AddSourceFilterForMoniker(dev.Mon, Nothing, dev.Name, capFilter)
DsError.ThrowExceptionForHR(hr)
baseGrabFlt = DirectCast(sampGrabber, IBaseFilter)
ConfigureSampleGrabber(sampGrabber)
' Add the frame grabber to the graph
hr = m_graphBuilder.AddFilter(baseGrabFlt, "Ds.NET Grabber")
DsError.ThrowExceptionForHR(hr)
' If any of the default config items are set
If (iFrameRate + iHeight + iWidth > 0) Then
SetConfigParms(capGraph, capFilter, iFrameRate, iWidth, iHeight)
End If
hr = capGraph.RenderStream(PinCategory.Capture, MediaType.Video, capFilter, baseGrabFlt, muxFilter)
DsError.ThrowExceptionForHR(hr)
'if you set the m_takePicture it won't
If Not m_takePicture then
'Set the output of the preview
hr = mediaEventEx.SetNotifyWindow(UseHand, WMGraphNotify, IntPtr.Zero)
DsError.ThrowExceptionForHR(hr)
'Set Owner to Display Video
hr = videoWindow.put_Owner(UseHand)
DsError.ThrowExceptionForHR(hr)
'Set window location - this was necessary so that the video didn't move down and to the right when you pushed the start/stop button
hr = videoWindow.SetWindowPosition(0, 0, my.Settings.previewwidth, my.Settings.previewHeight)
DsError.ThrowExceptionForHR(hr)
'Set Owner Video Style
hr = videoWindow.put_WindowStyle(WindowStyle.Child)
DsError.ThrowExceptionForHR(hr)
End If
SaveSizeInfo(sampGrabber)
Finally
If (Not fileWriterFilter Is Nothing) Then
Marshal.ReleaseComObject(fileWriterFilter)
fileWriterFilter = Nothing
End If
If (Not muxFilter Is Nothing) Then
Marshal.ReleaseComObject(muxFilter)
muxFilter = Nothing
End If
If (Not capFilter Is Nothing) Then
Marshal.ReleaseComObject(capFilter)
capFilter = Nothing
End If
If (Not sampGrabber Is Nothing) Then
Marshal.ReleaseComObject(sampGrabber)
sampGrabber = Nothing
End If
End Try
End Sub
' <summary> Read and store the properties </summary>
Private Sub SaveSizeInfo(ByVal sampGrabber As ISampleGrabber)
Dim hr As Integer
' Get the media type from the SampleGrabber
Dim media As AMMediaType = New AMMediaType()
hr = sampGrabber.GetConnectedMediaType(media)
DsError.ThrowExceptionForHR(hr)
If (Not (media.formatType.Equals(FormatType.VideoInfo)) AndAlso Not (media.formatPtr.Equals(IntPtr.Zero))) Then
Throw New NotSupportedException("Unknown Grabber Media Format")
End If
' Grab the size info
Dim vInfoHeader As VideoInfoHeader = New VideoInfoHeader()
Marshal.PtrToStructure(media.formatPtr, vInfoHeader)
m_videoWidth = vInfoHeader.BmiHeader.Width
m_videoHeight = vInfoHeader.BmiHeader.Height
m_stride = m_videoWidth * (vInfoHeader.BmiHeader.BitCount / 8)
DsUtils.FreeAMMediaType(media)
media = Nothing
End Sub
' <summary> Set the options on the sample grabber </summary>
Private Sub ConfigureSampleGrabber(ByVal sampGrabber As ISampleGrabber)
Dim hr As Integer
Dim media As AMMediaType = New AMMediaType()
media.majorType = MediaType.Video
media.subType = MediaSubType.RGB24
media.formatType = FormatType.VideoInfo
hr = sampGrabber.SetMediaType(media)
DsError.ThrowExceptionForHR(hr)
DsUtils.FreeAMMediaType(media)
media = Nothing
' Configure the samplegrabber callback
hr = sampGrabber.SetOneShot(false)
DsError.ThrowExceptionForHR(hr)
If m_takePicture then
hr = sampGrabber.SetCallback(Me, 0)
Else
hr = sampGrabber.SetCallback(Me, 0)
End If
DsError.ThrowExceptionForHR(hr)
DsError.ThrowExceptionForHR(hr)
'set the samplegrabber
sampGrabber.SetBufferSamples(False)
End Sub
' Set the Framerate, and video size
Private Sub SetConfigParms(ByVal capGraph As ICaptureGraphBuilder2, ByVal capFilter As IBaseFilter, ByVal iFrameRate As Integer, ByVal iWidth As Integer, ByVal iHeight As Integer)
Dim hr As Integer
Dim o As Object = Nothing
Dim media As AMMediaType = Nothing
Dim videoStreamConfig As IAMStreamConfig
Dim videoControl As IAMVideoControl = DirectCast(capFilter, IAMVideoControl)
' Find the stream config interface
hr = capGraph.FindInterface(PinCategory.Capture, MediaType.Video, capFilter, GetType(IAMStreamConfig).GUID, o)
videoStreamConfig = DirectCast(o, IAMStreamConfig)
Try
If (videoStreamConfig Is Nothing) Then
Throw New Exception("Failed to get IAMStreamConfig")
End If
' Get the existing format block
hr = videoStreamConfig.GetFormat(media)
DsError.ThrowExceptionForHR(hr)
' copy out the videoinfoheader
Dim v As VideoInfoHeader = New VideoInfoHeader()
Marshal.PtrToStructure(media.formatPtr, v)
' if overriding the framerate, set the frame rate
If (iFrameRate > 0) Then
v.AvgTimePerFrame = 10000000 / iFrameRate
End If
' if overriding the width, set the width
If (iWidth > 0) Then
v.BmiHeader.Width = iWidth
End If
' if overriding the Height, set the Height
If (iHeight > 0) Then
v.BmiHeader.Height = iHeight
End If
' Copy the media structure back
Marshal.StructureToPtr(v, media.formatPtr, False)
' Set the new format
hr = videoStreamConfig.SetFormat(media)
If hr<>0 then unsupportedVideo = True else unsupportedVideo=False
DsError.ThrowExceptionForHR(hr)
DsUtils.FreeAMMediaType(media)
media = Nothing
' Fix upsidedown video
If (Not videoControl Is Nothing) Then
Dim pCapsFlags As VideoControlFlags
Dim pPin As IPin = DsFindPin.ByCategory(capFilter, PinCategory.Capture, 0)
hr = videoControl.GetCaps(pPin, pCapsFlags)
DsError.ThrowExceptionForHR(hr)
If ((pCapsFlags & VideoControlFlags.FlipVertical) > 0) Then
hr = videoControl.GetMode(pPin, pCapsFlags)
DsError.ThrowExceptionForHR(hr)
hr = videoControl.SetMode(pPin, 0)
End If
End If
Finally
Marshal.ReleaseComObject(videoStreamConfig)
End Try
End Sub
' <summary> Shut down capture </summary>
Private Sub CloseInterfaces()
Dim hr As Integer
Try
If (Not m_mediaCtrl Is Nothing) Then
' Stop the graph
hr = m_mediaCtrl.Stop()
m_mediaCtrl = Nothing
m_bRunning = False
'Release Window Handle, Reset back to Normal
hr = videoWindow.put_Visible(OABool.False)
DsError.ThrowExceptionForHR(hr)
hr = videoWindow.put_Owner(IntPtr.Zero)
DsError.ThrowExceptionForHR(hr)
If mediaEventEx Is Nothing = False Then
hr = mediaEventEx.SetNotifyWindow(IntPtr.Zero, 0, IntPtr.Zero)
DsError.ThrowExceptionForHR(hr)
End If
End If
Catch ex As Exception
Debug.WriteLine(ex)
End Try
#If Debug Then
If (Not m_rot Is Nothing) Then
m_rot.Dispose()
m_rot = Nothing
End If
#End If
If (Not m_graphBuilder Is Nothing) Then
Marshal.ReleaseComObject(m_graphBuilder)
m_graphBuilder = Nothing
End If
GC.Collect()
End Sub
' <summary> sample callback, Originally not used - call this with integer 0 on the setcallback method </summary>
Function SampleCB(ByVal SampleTime As Double, ByVal pSample As IMediaSample) As Integer Implements ISampleGrabberCB.SampleCB
myTest = "In SampleCB"
Dim i As Integer=0
'jk added this code 10-22-13
if IsDBNull(pSample) =True then return -1
dim myLen As Integer = pSample.GetActualDataLength()
dim pbuf As IntPtr
if pSample.GetPointer(pbuf) = 0 AND mylen > 0 then
dim buf As byte()= new byte(myLen) {}
Marshal.Copy(pbuf, buf, 0, myLen)
'Alter the video - you could use this to adjust the brightness/red/green, etc.
'for i = myLen-1 to 0 step -1
' buf(i) = (255 - buf(i))
'Next i
If m_takePicture then
Dim bm As new Bitmap(m_videoWidth,m_videoHeight,Imaging.PixelFormat.Format24bppRgb)
Dim g_RowSizeBytes As Integer
Dim g_PixBytes() As Byte
mytest = "Execution point #1"
Dim m_BitmapData As BitmapData = Nothing
Dim bounds As Rectangle = New Rectangle(0, 0, m_videoWidth, m_videoHeight)
mytest = "Execution point #2"
m_BitmapData = bm.LockBits(bounds, Imaging.ImageLockMode.ReadWrite, Imaging.PixelFormat.Format24bppRgb)
mytest = "Execution point #4"
g_RowSizeBytes = m_BitmapData.Stride
mytest = "Execution point #5"
' Allocate room for the data.
Dim total_size As Integer = m_BitmapData.Stride * m_BitmapData.Height
ReDim g_PixBytes(total_size)
mytest = "Execution point #10"
'this writes the data to the Bitmap
Marshal.Copy(buf, 0, m_BitmapData.Scan0, mylen)
capturedPic=bm
mytest = "Execution point #15"
' Release resources.
bm.UnlockBits(m_BitmapData)
g_PixBytes = Nothing
m_BitmapData = Nothing
bm=Nothing
buf=Nothing
m_takePicture=False
captureSaved = True
mytest = "Execution point #20"
End If
End If
Marshal.ReleaseComObject(pSample)
Return 0
End Function
' <summary> buffer callback, Not used - call this with integer 1 on the setcallback method </summary>
Function BufferCB(ByVal SampleTime As Double, ByVal pBuffer As IntPtr, ByVal BufferLen As Integer) As Integer Implements ISampleGrabberCB.BufferCB
SyncLock Me
myTest = "In BufferCB"
End SyncLock
return 0
End Function
End Class
I'm sure this could be improved upon, but it works well for my purposes for now, and I hope it helps someone.
我确信这可以改进,但它现在对我的目的很有效,我希望它可以帮助某人。
回答by ??ssa P?ngj?rdenlarp
To display video you will need something like the VideoWindow
or BasicVideo2
filters which allows you to specify the windows control to draw the video on:
要显示视频,您需要像VideoWindow
或BasicVideo2
过滤器这样的东西,它允许您指定用于绘制视频的窗口控件:
iVidWdw.put_Owner(mVidCtl.Handle).
I have never gotten SampleGrabber
to work, but BasicVideo
, VMR9
and MediaDetector
can all capture (one or 2 might need MediaSeeking
). It sounds like you might need one filter to save to file (if you want that) and another to play/show, which is not uncommon. Also, you will almost certainly want to handle the getbitmap (snapshot) part in the C# Lib to convert the bmp data to a managed resource.
我从来没有SampleGrabber
开始工作,但是BasicVideo
,VMR9
并且MediaDetector
都可以捕获(一两个可能需要MediaSeeking
)。听起来你可能需要一个过滤器来保存到文件(如果你想要的话)和另一个来播放/显示,这并不少见。此外,您几乎肯定希望处理 C# Lib 中的 getbitmap(快照)部分以将 bmp 数据转换为托管资源。
Frame capture depends very much on the filter being used. As I said, I have had little luck with SampleGrabber
and the others all work differently. MOST return bitmap data NOTa bitmap and with some the image is upside down, so the data has to be transferred to a managed bitmap (MUCH faster in C#) and then rotated depending on the filter used.
帧捕获在很大程度上取决于所使用的过滤器。正如我所说,我运气不佳,SampleGrabber
其他人的工作方式都不同。大多数返回位图数据不是位图,并且有些图像是颠倒的,因此必须将数据传输到托管位图(在 C# 中要快得多),然后根据使用的过滤器进行旋转。
If you got SampleGrabber
to work (thats all it does it grab images - maybe clips, I forget), you should be able to save bitmaps/captures - I am not sure what the status there is based on the post. It sort of sounds like you have 3 issues: a) capturing the camera stream b) saving the stream to disk (?) and then c) Getting snapshots
如果您开始SampleGrabber
工作(这就是它抓取图像的全部功能-也许是剪辑,我忘记了),您应该能够保存位图/捕获-我不确定基于帖子的状态。听起来你有 3 个问题:a)捕获相机流 b)将流保存到磁盘(?)然后 c)获取快照
One other detail about frame caps is that SOME filters require the graph to be paused in order to capture. That is probably suboptimal for a webcam stream, so it would rule out 1 or 2 (BasicVideo2
is at least one of them).
关于帧上限的另一个细节是某些过滤器需要暂停图形以进行捕获。这对于网络摄像头流来说可能不是最理想的,因此它会排除 1 或 2(BasicVideo2
至少是其中之一)。
回答by Dee Mon
Here's a short example of working with images of a webcam video stream in C# and DirectShow using Sample Grabber:
下面是使用 Sample Grabber 在 C# 和 DirectShow 中处理网络摄像头视频流图像的简短示例:
http://www.infognition.com/blog/2013/working_with_raw_video_in_c_sharp_directshow.html
http://www.infognition.com/blog/2013/working_with_raw_video_in_c_sharp_directshow.html
All you need is to get data in suitable format (RGB24 or RGB32, not YUY2) either by setting it as preferred format of sample grabber or converting the data as you receive it. Then saving it to a jpeg/bitmap file is trivial.
您所需要的只是通过将数据设置为样本采集器的首选格式或在接收数据时转换数据,以合适的格式(RGB24 或 RGB32,而不是 YUY2)获取数据。然后将其保存到 jpeg/位图文件是微不足道的。