C# VB.net WPF利用MediaFoundation打开摄像头捕捉图片
来源:互联网 发布:python rest接口测试 编辑:程序博客网 时间:2024/06/08 13:27
主要代码 以同步的方式获得帧
REM MediaFoundation的.net 类库 http://mfnet.sourceforge.netImports MediaFoundationImports System.Runtime.InteropServicesImports System.IOImports System.DrawingImports System.Drawing.ImagingClass MainWindow Private mediaSource As IMFMediaSource Private attribute As IMFAttributes Private activateDevices() As IMFActivate Private deviceName As String REM 设备名字 ''' <summary> ''' 打开摄像头设备 ''' </summary> Private Sub OpenCaptureDevice() Dim result As Integer result = MFExtern.MFCreateAttributes(attribute, 1) REM 创建一个属性 If (result <> 0) Then Return attribute.SetGUID(MFAttributesClsid.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, CLSID.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID) REM 设置属性 Dim devicescount As Integer MFExtern.MFEnumDeviceSources(attribute, activateDevices, devicescount) REM 枚举满足属性的摄像头设备 If (result <> 0) Then Return If (devicescount = 0) Then Return activateDevices(0).GetAllocatedString(MFAttributesClsid.MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, deviceName, 0) Console.WriteLine(deviceName) activateDevices(0).ActivateObject(GetType(IMFMediaSource).GUID, mediaSource) REM 激活设备 End Sub Private presentationDescriptor As IMFPresentationDescriptor = Nothing Private streamDescriptor As IMFStreamDescriptor = Nothing Private mediatypeHandler As IMFMediaTypeHandler = Nothing Private mediatypeCount As Integer Private mediaType As IMFMediaType = Nothing ''' <summary> ''' 枚举摄像头支持的参数,选择匹配的参数捕捉图像 ''' </summary> ''' <param name="width"></param> ''' <param name="height"></param> ''' <param name="fps"></param> ''' <param name="typename"></param> Private Sub SetupCatureDevice(width As Integer, height As Integer, fps As Double, typename As String) Dim result As Integer REM 创建SourceReader result = MFExtern.MFCreateSourceReaderFromMediaSource(mediaSource, attribute, sourcereader) If (result <> 0) Then Return REM 获得一个表现描述符 result = mediaSource.CreatePresentationDescriptor(presentationDescriptor) If (result <> 0) Then Return Dim bselected As Boolean REM 从表现描述符中获得流描述符 result = presentationDescriptor.GetStreamDescriptorByIndex(0, bselected, streamDescriptor) If (result <> 0) Then Return REM 从流描述器中或得媒体类型操作器 result = streamDescriptor.GetMediaTypeHandler(mediatypeHandler) If (result <> 0) Then Return REM 获得支持的媒体类型 result = mediatypeHandler.GetMediaTypeCount(mediatypeCount) If (result <> 0) Then Return For i = 0 To mediatypeCount - 1 REM 遍历媒体类型,选择合适的读取 result = mediatypeHandler.GetMediaTypeByIndex(i, mediaType) If (result <> 0) Then Continue For Dim framesize As UInt64 mediaType.GetUINT64(MFAttributesClsid.MF_MT_FRAME_SIZE, framesize) Dim w, h As UInt32 w = (framesize >> 32).ToString() h = (framesize And &H0FFFFFFF) Dim framerate As UInt64 Dim frame As Int32 = (framerate >> 32).ToString() Dim ratio As Int32 = (framerate And &H00000000FFFFFFFFL).ToString() mediaType.GetUINT64(MFAttributesClsid.MF_MT_FRAME_RATE, framerate) Dim samplesize As Int32 mediaType.GetUINT32(MFAttributesClsid.MF_MT_SAMPLE_SIZE, samplesize) Dim subtype As Guid mediaType.GetGUID(MFAttributesClsid.MF_MT_SUBTYPE, subtype) Console.WriteLine(w.ToString + " x " + h.ToString() + " @ " + (frame / ratio).ToString("f1") + "hz" _ + vbTab + "samplesize:" + samplesize.ToString() _ + vbTab + "type:" + NameofGUID(subtype)) If (w = width And h = height And frame / ratio = fps And NameofGUID(subtype) = typename) Then result = mediatypeHandler.SetCurrentMediaType(mediaType) If (result <> 0) Then Return End If Next End Sub ''' <summary> ''' 工具函数,根据GUID 找名字 ''' </summary> ''' <param name="guid"></param> ''' <returns></returns> Private Function NameofGUID(guid As Guid) As String Dim names() As Reflection.FieldInfo = GetType(MFMediaType).GetFields() For Each i In names Dim obj As Object = i.GetValue(Nothing) If TypeOf (obj) Is Guid AndAlso obj = guid Then Return i.Name End If Next Return "unknown" End Function Dim bufptr As IntPtr ''' <summary> ''' 开始捕捉 ''' </summary> Private Sub CaptureStart() Dim samplesize As Int32 If (mediaType Is Nothing) Then Throw New Exception("mediatype invailed") mediaType.GetUINT32(MFAttributesClsid.MF_MT_SAMPLE_SIZE, samplesize) REM 获得一个样本的大小 bufptr = Marshal.AllocHGlobal(samplesize) REM 设置图片的Buffer大小 Dim var As New Misc.PropVariant() mediaSource.Start(presentationDescriptor, Nothing, var) End Sub ''' <summary> ''' 停止捕捉 ''' </summary> Private Sub CaptureStop() mediaSource.Stop() Marshal.FreeHGlobal(bufptr) End Sub Private sourcereader As MediaFoundation.ReadWrite.IMFSourceReader Private streamindex As Integer Private streamflags As MediaFoundation.ReadWrite.MF_SOURCE_READER_FLAG Private timestamp As Long Private mfsample As IMFSample ''' <summary> ''' 捕捉一帧 ''' </summary> Private Sub Capture() Dim result As Integer result = sourcereader.ReadSample(0, ReadWrite.MF_SOURCE_READER_CONTROL_FLAG.None, streamindex, streamflags, timestamp, mfsample) If (result <> 0) Then Return If (mfsample Is Nothing) Then Return Dim samplesize As Int32 mediaType.GetUINT32(MFAttributesClsid.MF_MT_SAMPLE_SIZE, samplesize) Dim mediabuf As IMFMediaBuffer = Nothing MFExtern.MFCreateMemoryBuffer(samplesize, mediabuf) mfsample.CopyToBuffer(mediabuf) REM 拷贝一个样本到MediaBuffer Dim maxlen, curlen As Integer REM 图像大小max,jpg的大小cur,压缩jpg大小不定 mediabuf.GetMaxLength(maxlen) mediabuf.GetCurrentLength(curlen) mediabuf.Lock(bufptr, maxlen, curlen) REM 锁定 MediaBuffer Dim managebuf(maxlen - 1) As Byte Marshal.Copy(bufptr, managebuf, 0, maxlen) REM 从MediaBuffer拷贝到自定的图片buffer REM 转换buffer成BitmapSource Dim stream As New MemoryStream(managebuf.Length) stream.Seek(0, SeekOrigin.Begin) stream.Write(managebuf, 0, managebuf.Length) stream.Seek(0, SeekOrigin.Begin) imagesource = StreamToBitmapSource(stream) stream.Close() mediabuf.Unlock() End Sub Private imagesource As ImageSource Private timer As New Windows.Threading.DispatcherTimer Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs) Width = 1280 Height = 720 Left = 0 Top = 0 OpenCaptureDevice() SetupCatureDevice(1280, 720, 30, "MJPG") CaptureStart() timer.Interval = TimeSpan.FromMilliseconds(10) AddHandler timer.Tick, AddressOf TimerTick timer.Start() End Sub Private Sub TimerTick(sender As Object, e As EventArgs) Capture() Dim bs As BitmapSource = imagesource Me.Background = New ImageBrush(bs) End Sub ''' <summary> ''' 流转成BitmapSource ''' </summary> ''' <param name="s">流</param> ''' <returns></returns> Public Shared Function StreamToBitmapSource(s As Stream) As BitmapSource Dim jpg As New JpegBitmapDecoder(s, BitmapCreateOptions.None, BitmapCacheOption.OnLoad) REM OnLoad很重要 Return jpg.Frames(0) End FunctionEnd Class
还可以使用异步方式,以异步的方式请求,重写函数给Mediafoundation回调来获得帧:
Imports MediaFoundationImports MediaFoundation.ReadWriteImports MediaFoundation.MiscImports System.IOPublic Class MFDevice Implements IDisposable Private mActivator As IMFActivate Private mFriendlyName As String Private mSymbolicName As String Public Sub New(a As IMFActivate) mActivator = a mFriendlyName = Nothing mFriendlyName = Nothing End Sub Public ReadOnly Property Activator As IMFActivate Get Return mActivator End Get End Property Public ReadOnly Property Name As String Get If (mFriendlyName Is Nothing) Then Dim size As Integer = 0 Result = mActivator.GetAllocatedString(MFAttributesClsid.MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, mFriendlyName, size) End If Return mFriendlyName End Get End Property Public ReadOnly Property SymbolicName As String Get If (mFriendlyName Is Nothing) Then Dim size As Integer = 0 Result = mActivator.GetAllocatedString(MFAttributesClsid.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, mSymbolicName, size) End If Return mSymbolicName End Get End Property Public Shared Function GetVideoCaptureDevices() As MFDevice() Dim devices() As IMFActivate = Nothing Dim attrib As IMFAttributes = Nothing Result = MFExtern.MFCreateAttributes(attrib, 1) attrib.SetGUID(MFAttributesClsid.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, CLSID.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID) Dim devicesCount As Integer Result = MFExtern.MFEnumDeviceSources(attrib, devices, devicesCount) Dim mfdevices(devicesCount) As MFDevice For i = 0 To devicesCount - 1 mfdevices(i) = New MFDevice(devices(i)) Next If attrib Is Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(attrib) End If Return mfdevices End Function#Region "IDisposable Support" Private disposedValue As Boolean ' 要检测冗余调用 ' IDisposable Protected Overridable Sub Dispose(disposing As Boolean) If Not disposedValue Then If disposing Then ' TODO: 释放托管状态(托管对象)。 If (mActivator IsNot Nothing) Then System.Runtime.InteropServices.Marshal.ReleaseComObject(mActivator) mActivator = Nothing End If mFriendlyName = Nothing mSymbolicName = Nothing GC.SuppressFinalize(Me) End If ' TODO: 释放未托管资源(未托管对象)并在以下内容中替代 Finalize()。 ' TODO: 将大型字段设置为 null。 End If disposedValue = True End Sub ' TODO: 仅当以上 Dispose(disposing As Boolean)拥有用于释放未托管资源的代码时才替代 Finalize()。 'Protected Overrides Sub Finalize() ' ' 请勿更改此代码。将清理代码放入以上 Dispose(disposing As Boolean)中。 ' Dispose(False) ' MyBase.Finalize() 'End Sub ' Visual Basic 添加此代码以正确实现可释放模式。 Public Sub Dispose() Implements IDisposable.Dispose ' 请勿更改此代码。将清理代码放入以上 Dispose(disposing As Boolean)中。 Dispose(True) ' TODO: 如果在以上内容中替代了 Finalize(),则取消注释以下行。 End Sub#End RegionEnd ClassPublic Class MFCaptureAsync Inherits COMBase Implements ReadWrite.IMFSourceReaderCallback Implements IDisposable Private mSourceReaderAsync As Alt.IMFSourceReaderAsync Private mSymbolicLink As String Private mMediatype As IMFMediaType Public Sub New(dispatcher As System.Windows.Threading.Dispatcher) mSourceReaderAsync = Nothing mSymbolicLink = Nothing MFExtern.MFStartup(&H20070, MFStartup.Lite) Me.dispatcher = dispatcher End Sub Public Sub SetDevice(device As MFDevice, width As Integer, height As Integer, fps As Integer, fmt As String) Dim activate As IMFActivate = device.Activator Dim mediaSource As IMFMediaSource = Nothing Dim attrib As IMFAttributes = Nothing SyncLock Me Try CloseDevice() Result = activate.ActivateObject(GetType(IMFMediaSource).GUID, mediaSource) mSymbolicLink = device.SymbolicName Result = MFExtern.MFCreateAttributes(attrib, 1) Result = attrib.SetUnknown(MFAttributesClsid.MF_SOURCE_READER_ASYNC_CALLBACK, Me) Dim sourcereader As IMFSourceReader = Nothing Result = MFExtern.MFCreateSourceReaderFromMediaSource(mediaSource, attrib, sourcereader) mSourceReaderAsync = sourcereader Dim i As Integer = 0 Do Dim mediatype As IMFMediaType = Nothing Result = mSourceReaderAsync.GetNativeMediaType(MF_SOURCE_READER.FirstVideoStream, i, mediatype) If Failed(Result) Then Exit Do End If Try Result = TryMediaType(mediatype, width, height, fps, fmt) If Result = HResult.S_OK Then mMediatype = mediatype Exit Do End If Catch ex As Exception Console.WriteLine(ex.ToString()) Finally SafeRelease(mediatype) End Try i += 1 Loop While True Result = mSourceReaderAsync.ReadSample(MF_SOURCE_READER.FirstVideoStream, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero) If Failed(Result) Then If mediaSource IsNot Nothing Then mediaSource.Shutdown() End If CloseDevice() End If Catch ex As Exception Console.WriteLine(ex.ToString()) Finally SafeRelease(mediaSource) SafeRelease(attrib) End Try End SyncLock End Sub Public Function TryMediaType(mediaType As IMFMediaType, width As Integer, height As Integer, fps As Integer, name As String) Dim framesize As UInt64 Dim framerate As UInt64 Dim subtype As Guid Result = mediaType.GetUINT64(MFAttributesClsid.MF_MT_FRAME_SIZE, framesize) Result = mediaType.GetUINT64(MFAttributesClsid.MF_MT_FRAME_RATE, framerate) Result = mediaType.GetGUID(MFAttributesClsid.MF_MT_SUBTYPE, subtype) Dim w, h As UInt32 w = (framesize >> 32) h = (framesize And &H00000000FFFFFFFFL) Dim frame As Int32 = (framerate >> 32) Dim ratio As Int32 = (framerate And &H00000000FFFFFFFFL) Dim samplesize As Int32 Result = mediaType.GetUINT32(MFAttributesClsid.MF_MT_SAMPLE_SIZE, samplesize) Console.WriteLine(w.ToString + " x " + h.ToString() + " @ " + (frame / ratio).ToString("f1") + "hz" _ + vbTab + "samplesize:" + samplesize.ToString() _ + vbTab + "type:" + NameofGUID(subtype)) If (w = width And h = height And frame / ratio = fps And NameofGUID(subtype) = name) Then Result = mSourceReaderAsync.SetCurrentMediaType(MF_SOURCE_READER.FirstVideoStream, Nothing, mediaType) mediaType.GetUINT32(MFAttributesClsid.MF_MT_SAMPLE_SIZE, samplesize) REM 获得一个样本的大小 bufptr = System.Runtime.InteropServices.Marshal.AllocHGlobal(samplesize) REM 设置图片的Buffer大小 Return HResult.S_OK End If Return HResult.S_FALSE End Function Private Function NameofGUID(guid As Guid) As String Dim names() As Reflection.FieldInfo = GetType(MFMediaType).GetFields() For Each i In names Dim obj As Object = i.GetValue(Nothing) If TypeOf (obj) Is Guid AndAlso obj = guid Then Return i.Name End If Next Return "unknown" End Function Public Sub CloseDevice() SyncLock Me SafeRelease(mSourceReaderAsync) mSourceReaderAsync = Nothing mSymbolicLink = Nothing End SyncLock End Sub Public Shared Function StreamToBitmapSource(s As Stream) As BitmapSource Dim jpg As New JpegBitmapDecoder(s, BitmapCreateOptions.None, BitmapCacheOption.OnLoad) REM OnLoad很重要 Return jpg.Frames(0) End Function Private bufptr As IntPtr Private imagesource As ImageSource Private dispatcher As System.Windows.Threading.Dispatcher Public Delegate Sub OnReadSampleHandler(hrStatus As HResult, dwStreamIndex As Integer, dwStreamFlags As MF_SOURCE_READER_FLAG, llTimestamp As Long, pSample As IMFSample) Public Event OnCaptureEvent As EventHandler(Of ImageSource) Public Function OnReadSample(hrStatus As HResult, dwStreamIndex As Integer, dwStreamFlags As MF_SOURCE_READER_FLAG, llTimestamp As Long, pSample As IMFSample) As HResult Implements IMFSourceReaderCallback.OnReadSample SyncLock Me Dim mediabuffer As IMFMediaBuffer = Nothing Try If Succeeded(hrStatus) Then If pSample IsNot Nothing Then Result = pSample.GetBufferByIndex(0, mediabuffer) Else GoTo done End If Else GoTo done End If Dim maxlen, curlen As Integer REM 图像大小max,jpg的大小cur,压缩jpg大小不定 mediabuffer.GetMaxLength(maxlen) mediabuffer.GetCurrentLength(curlen) mediabuffer.Lock(bufptr, maxlen, curlen) REM 锁定 MediaBuffer Dim managebuf(maxlen - 1) As Byte System.Runtime.InteropServices.Marshal.Copy(bufptr, managebuf, 0, maxlen) REM 从MediaBuffer拷贝到自定的图片buffer REM 转换buffer成BitmapSource Dim stream As New MemoryStream(managebuf.Length) stream.Seek(0, SeekOrigin.Begin) stream.Write(managebuf, 0, managebuf.Length) stream.Seek(0, SeekOrigin.Begin) imagesource = StreamToBitmapSource(stream) stream.Close() mediabuffer.Unlock() dispatcher.Invoke(Sub() RaiseEvent OnCaptureEvent(Me, imagesource) End Sub)done: REM RCW Runtime Callable Wrapper REM STAThread MTAThread Catch ex As Exception Console.WriteLine(ex.ToString()) Finally SafeRelease(mediabuffer) SafeRelease(pSample) End Try End SyncLock Return 0 End Function Public Sub Request() Result = mSourceReaderAsync.ReadSample(MF_SOURCE_READER.FirstVideoStream, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero) End Sub Public Function OnFlush(dwStreamIndex As Integer) As HResult Implements IMFSourceReaderCallback.OnFlush Return HResult.S_OK End Function Public Function OnEvent(dwStreamIndex As Integer, pEvent As IMFMediaEvent) As HResult Implements IMFSourceReaderCallback.OnEvent Return HResult.S_OK End Function#Region "IDisposable Support" Private disposedValue As Boolean ' 要检测冗余调用 ' IDisposable Protected Overridable Sub Dispose(disposing As Boolean) If Not disposedValue Then If disposing Then ' TODO: 释放托管状态(托管对象)。 End If ' TODO: 释放未托管资源(未托管对象)并在以下内容中替代 Finalize()。 ' TODO: 将大型字段设置为 null。 End If disposedValue = True End Sub ' TODO: 仅当以上 Dispose(disposing As Boolean)拥有用于释放未托管资源的代码时才替代 Finalize()。 'Protected Overrides Sub Finalize() ' ' 请勿更改此代码。将清理代码放入以上 Dispose(disposing As Boolean)中。 ' Dispose(False) ' MyBase.Finalize() 'End Sub ' Visual Basic 添加此代码以正确实现可释放模式。 Public Sub Dispose() Implements IDisposable.Dispose ' 请勿更改此代码。将清理代码放入以上 Dispose(disposing As Boolean)中。 Dispose(True) ' TODO: 如果在以上内容中替代了 Finalize(),则取消注释以下行。 ' GC.SuppressFinalize(Me) End Sub#End RegionEnd Class
重写OnReadSample函数,Mediafoundation这个COM组件是Both套件,但是VB.net只能使用STA套间,C#可以使用MTA套间,但是WPF是不允许用MTA的。(STA套间这个模式是好一点的吧?,这个问题弄了我好久,一开始还以为我的代码有问题)STA套间的组件属于创建的线程,OnReadSample不是主线程回调,而是其他线程回调进去的。所以不要在OnReadSample中使用主线程的组件接口,也不要在主线程使用其他线程的组件接口。但MTA是可以的。不然会出现一些异常System.__ComObject”的 COM 对象强制转换为接口类型。
MainWindow.xaml.vb的部分代码
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs) Width = SystemParameters.PrimaryScreenWidth Height = SystemParameters.PrimaryScreenHeight Left = 0 Top = 0 Dim device As MFDevice = MFDevice.GetVideoCaptureDevices()(0) mfcapasync = New MFCaptureAsync(Me.Dispatcher) AddHandler mfcapasync.OnCaptureEvent, AddressOf OnCapture mfcapasync.SetDevice(device, 1920, 1080, 30, "MJPG") mfcapasync.Request() Play() End Sub Public Sub OnCapture(sender As Object, e As ImageSource) grid.Background = New ImageBrush(e) mfcapasync.Request() End Sub
在开始的时候请求一帧,在捕捉到一帧时请求下一帧。迟点再放出整个工程源码。
0 0
- C# VB.net WPF利用MediaFoundation打开摄像头捕捉图片
- c#利用windowsapi捕捉屏幕图片
- c# 利用AForge.NET组件操作摄像头
- c# 利用AForge.NET组件操作摄像头
- C# 摄像头视频捕捉 (使用DirectX.Capture)
- C# 利用AForge.NET 调用电脑摄像头进行拍照
- 利用Mat数据结构打开摄像头
- VB.Net摄像头编程类
- [VB.NET]vb.net如何捕捉摄相头的视频
- opencv 打开摄像头却捕捉不到图像解决方法
- C# .net 利用QRCode生成二维码图片
- 一个驱动webcam的类,利用通用的摄像头驱动程序avicap32.dll [vb.net]
- 一个驱动webcam的类,利用通用的摄像头驱动程序avicap32.dll [vb.net]
- vb.net wpf不规则窗体
- 利用Flash获取摄像头视频进行动态捕捉
- 利用Qt与OpenCV简单实现摄像头图像捕捉
- 利用Flash获取摄像头视频进行动态捕捉
- DirectShow:图片的抓取---从摄像头流中捕捉一张图片zzDirectshow中的视频捕捉
- Qt之findChildren()
- 安卓权限:用户的注意力、理解和行为
- java IO,字符字节终极理解
- informatica 9.6.1安装“坑”列表
- java join的用法
- C# VB.net WPF利用MediaFoundation打开摄像头捕捉图片
- Python中写入csv和txt
- android json解析使用总结(一)
- 软件生命周期
- Java设计模式----观察者模式(Observer)
- docker ubuntu容器更换阿里源
- java UUID生成唯一标识符
- Mac Homebrew 常用命令
- android json解析使用总结(二)-—天气预报的实现