2013-10-19 52 views
2

我想在Visual Studio 2008中编写一个程序,它将访问网络摄像头,在屏幕上显示预览,然后保存静态快照(.jpg )按下按钮时。后来我将把它与数据库集成,但我不应该对这个部分有任何问题。在做了一些研究后,看起来DirectShow是最好的选择,因为WIA没有在我的相机上工作(我不确定它会在未来继续工作)。最好,我需要我的解决方案,从Windows XP工作到Windows 7. 我从来没有使用过DirectShow(或类似的)。我遇到的一个问题是,大部分代码都是用C#编写的,这是我从未学过的。我找到了一个也使用vb.net的DirectShow.Net库,所以这很有帮助,但我仍然遇到问题。 下面的代码是从库中的示例中获取的,并且可以工作,但我想稍微改变它,但无法使其正常工作。该代码现在将相机捕捉保存到文件中。我可以删除“capGraph.SetOutputFileName”这一行,该视频只会启动到它自己的窗口中,但我不怎么控制它。基本上,我想知道如何做两件事:在DirectShow中预览相机并捕获静止图像 - 在VB.net中

  1. 如何让DirectShow显示在我指定的窗体(picturebox?)上的控件上?
  2. 然后我可以在用户点击一个按钮(它可以暂停视频或任何其他内容)时获得该视频的快照,因为那时我不需要预览即可恢复,至少不会持续几秒钟)

非常感谢,对不起,如果其中一些措辞不是很好。我是自学成才的,在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 
+0

要显示视频,您需要像'VideoWindow'或'BasicVideo2'过滤器,它允许您指定窗口控件来绘制视频:'iVidWdw.put_Owner(mVidCtl.Handle)'。我从来没有得到SampleGrabber的工作,但BasicVideo,VMR9和MediaDetector都可以捕获(一个或两个可能需要'MediaSeeking')。这听起来像你可能需要一个过滤器来保存到文件(如果你想要)和另一个过滤器来播放/显示,这并不罕见。另外,您几乎可以肯定要处理C#Lib中的getbitmap(快照)部分,以将bmp数据转换为托管资源。 – Plutonix

+0

感谢您的信息,Plutonix。我能够使用.put_Owner等正确显示视频。现在我只是在处理快照部分。我可以轻松地从预览摄像机的输出切换到写入avi文件。是否没有办法轻易地将输出捕获为单帧jpeg/bitmap(将其写入文件或将其抓到cliboard等)?一旦框架被抓住,我将不会有任何问题将其保存到文件。 –

+0

有另一个长期的评论 - 我将转换为答案,因为a)它有帮助和b)允许更长的解释 – Plutonix

回答

2

这是我最终用于我的项目 - 它给你一个预览窗口,然后在另一个窗体上,你可以按下一个按钮来拍照。我拍摄照片并将静止图像显示为主窗体上的另一个图像框(稍大一些)。我添加了一个裁剪框来选择你最终想要保存的那个图片框的哪一部分,但是为了简单起见,我并没有在这个答案中包含它。

您与呼叫初始化:cam = New Capture(my.Settings.VideoDeviceNum, FRAMERATE, my.Settings.CapturePictureWidth, my.Settings.CapturePictureHeight)cam.Start()

快照将采取与此呼吁:

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." 

的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 

主要相机类如下:

'**************************************************************************** 
'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 

我敢肯定,这可以改进,但它运作良好,我的目的就目前而言,我希望它可以帮助别人。

+2

嗨,杰里米,这太棒了!任何你可以分享整个项目的机会!这正是我一直在寻找的 – Jeanno

0

要显示的视频,你会需要的东西,如VideoWindowBasicVideo2过滤器,它允许你指定窗口控制上绘制视频:

iVidWdw.put_Owner(mVidCtl.Handle). 

我从来没有得到SampleGrabber工作,但BasicVideo,VMR9MediaDetector都可以捕获(一个或2个可能需要MediaSeeking)。这听起来像你可能需要一个过滤器来保存到文件(如果你想要)和另一个过滤器来播放/显示,这并不罕见。另外,您几乎可以肯定要处理C#Lib中的getbitmap(快照)部分,以将bmp数据转换为托管资源。

帧捕获很大程度上取决于所使用的滤波器。正如我所说,我对SampleGrabber有一点运气,其他人的工作方式都不一样。 MOST返回位图数据NOT一个位图,并与一些图像倒置,所以数据必须传输到一个托管位图(在C#更快),然后根据所使用的过滤器旋转。

如果你有SampleGrabber工作(那所做的只是抢图片 - 也许剪辑,我忘了),你应该能够保存位图/捕获 - 我不知道的状态有立足岗位什么。它听起来像你有三个问题:a)捕获相机流b)将流保存到磁盘(?),然后c)获取快照

关于帧大小的另一个细节是,某些过滤器需要图形暂停以捕捉。这对于网络摄像头流来说可能不太理想,所以它会排除1或2(BasicVideo2至少是其中之一)。

0

下面是用C#和使用样品采集卡网络摄像头视频流的图像DirectShow的工作的一个简单的例子:

http://www.infognition.com/blog/2013/working_with_raw_video_in_c_sharp_directshow.html

所有你需要的,就是以合适的格式(RGB24或RGB32,数据未YUY2),可以将其设置为采样器的首选格式,也可以在收到数据时进行转换。然后将它保存到一个JPEG /位图文件是微不足道的。

+0

谢谢,迪。我能够实现这一点,但在转换到位图部分时遇到了麻烦。我认为这会比看起来更简单。我更新了代码,它在调用m_BitmapData = bm.LockBits(bounds,Imaging.ImageLockMode.ReadWrite,Imaging.PixelFormat.Format24bppRgb)时失败。 –

+0

我得到了它的工作 - 现在必须修复其他一些小问题,但我会一旦设置完成,将其全部发布。 –