Gestione della periferica
La classe Base_VideoAcquireHardware
Nel post precedente siamo riusciti ad enumerare le periferiche video ed a crearci una lista indicizzata in cui ad ogni intero corrisponde un moniker. Le periferiche disponibili, quindi, saranno numerate da 0 in poi.
Adesso vogliamo crearci un oggetto che ci permetta di avviare e bloccare ( successivamente aggiungeremo metodi nuovi) il flusso video. Questo oggetto dovrà nascondere tutte le operazioni necessarie per la gestione della periferica e fornire solo i metodi che in realtà ci interessano, tra cui:
- Inizializzazione dell'hardware , StartVideo, StopVideo e DistruzioneHardware ( per rilasciarlo alle altre applicazioni ).
Come destinazione del flusso utilizzeremo un qualsiasi oggetto dotato di handle ( in realtà deve essere un oggetto di tipo Window (picturebox, form, etc..).
Classe Base_VideoAcquireHardware - Panoramica
Personalmente ho battezzato la classe Base_VideoAcquireHardware ma come sempre un nome vale l'altro.. In linea generale questa classe conterrà un riferimento al Moniker, un riferimento all'indice, gli oggetti DirectShow necessari per collegarsi alla periferica e alcuni metodi...
- Costruttore new([arg]...): Inizializza l'hardware e gli oggetti DirectShow necessari.
- StartVideoOnHandle([arg]...) : Avvia la visualizzazione del flusso nell'oggetto che gli passeremo come parametro... Questa funzione può essere richiamata solo 1 volta finchè non viene richiamata la StopVideo...
- StopVideo(): Blocca momentaneamente il flusso..
- Dispose() : Distrugge gli oggetti DirectShow,libera la periferica e finalizza la classe.
- ResizeWindowOnHandle([arg]...) : Questa funzione và richiamata dal programma che utilizza la nostra classe. Quando si ridimensiona una picturebox,un form, o comunque l'oggetto dove stiamo visualizzando il video è necessario richiamarla per informare DirectShow e ridimensionare così anche la dimensione del video. (se avremo tempo cercheremo di far rilevare in automatico, alla classe, il resize dell'oggetto di destinazione)
Vediamo quindi come è definita la nostra classe:
Classe Base_VideoAcquireHardware - Implementazione VB.NET
Public Class Base_VideoAcquireHardware
' Indice e riferimento all'oggetto COM che identifica l'hardware video
Private DeviceID As Integer
Private Ref_Moniker As IMoniker
Private DeviceDescription As String
' Oggetti DirectSHOW per l'utilizzo dell'hardware
Private MyGraphBuilder As IGraphBuilder
Private MyCaptureGraphBuilder As ICaptureGraphBuilder2
Private MyWinDevice As IVideoWindow
Private MyMediaControl As IMediaControl
' Oggetto IBaseFilter recuperato tramite il moniker. (Il moniker verrà bindato su questa classe per usarne i metodi)
Private MyHardware As IBaseFilter
' Variabile per capire se è già rendered una periferica
Private IsRender As Boolean = False
Private IsDisposed As Boolean = False
'Il costruttore si occupa di inizializzare gli oggetti COM necessari.
Public Sub New(ByVal HardwareIndex As Integer, ByRef HardwareMoniker As IMoniker)
Dim ErrH As Integer
' Riferimenti BASE
DeviceID = HardwareIndex
Ref_Moniker = HardwareMoniker
'Legge la descrizione
Dim IProp As IPropertyBag = Nothing
HardwareMoniker.BindToStorage(Nothing, Nothing, GetType(IPropertyBag).GUID, IProp)
IProp.Read("FriendlyName", DeviceDescription, Nothing)
' Genera gli oggetti COM necessari per il funzionamento di DirectShow. Binda il Moniker
' in oggetto IBaseFilter. (Filtro di cui è possibile effettuare l'attach sul grafo Dshow)
Dim source As Object = Nothing
HardwareMoniker.BindToObject(Nothing, Nothing, GetType(IBaseFilter).GUID, source)
MyHardware = CType(source, IBaseFilter)
MyGraphBuilder = CType(New FilterGraph, IGraphBuilder)
MyMediaControl = CType(MyGraphBuilder, IMediaControl)
MyCaptureGraphBuilder = CType(New CaptureGraphBuilder2, ICaptureGraphBuilder2)
MyWinDevice = CType(MyGraphBuilder, IVideoWindow)
' A questo punto l'oggetto è pronto per utilizzare la periferica video
' Effettua gli attach richiesti sul grafo
ErrH = MyCaptureGraphBuilder.SetFiltergraph(MyGraphBuilder)
DsError.ThrowExceptionForHR(ErrH)
ErrH = MyGraphBuilder.AddFilter(MyHardware, "VideoAcquire_" & HardwareIndex)
DsError.ThrowExceptionForHR(ErrH)
' Libera la memoria
Marshal.ReleaseComObject(IProp)
End Sub
' Questa funzione avvia il flusso video su un handle di finestra.
' I parametri 2 e 3 permettono di definire da dove acquisisire ( da quale PIN) e il tipo di MEDIA
Public Sub StartVideoOnHandle(ByRef __dstHandle As System.IntPtr, ByVal PinType As DsGuid, ByVal MediaT As DsGuid)
Dim ErrH As Integer
If Me.IsDisposed Then
Throw New Exception("Questo oggetto è stato eliminato. E' necessario istanziare un nuovo oggetto.")
End If
If Not IsRender Then
ErrH = MyCaptureGraphBuilder.RenderStream(PinType, MediaT, MyHardware, Nothing, Nothing)
DsError.ThrowExceptionForHR(ErrH)
Marshal.ReleaseComObject(MyHardware)
IsRender = True
End If
ErrH = MyMediaControl.Run()
DsError.ThrowExceptionForHR(ErrH)
ErrH = MyWinDevice.put_Owner(__dstHandle)
DsError.ThrowExceptionForHR(ErrH)
ErrH = MyWinDevice.put_WindowStyle(WindowStyle.Child Or WindowStyle.ClipChildren)
DsError.ThrowExceptionForHR(ErrH)
ErrH = MyWinDevice.put_Visible(OABool.True)
DsError.ThrowExceptionForHR(ErrH)
End Sub
' Questa funzione andrebbe richiamata ogni qual volta viene effettuato un resize
' dell'oggetto puntato dall'handle.
Public Sub ResizeVideoOnHandle(ByRef __dstHandle As System.IntPtr, ByVal Width As Integer, ByVal Height As Integer)
If Me.IsDisposed Then
Throw New Exception("Questo oggetto è stato eliminato. E' necessario istanziare un nuovo oggetto.")
End If
If Not IsNothing(__dstHandle) Then
MyWinDevice.SetWindowPosition(0, 0, Width, Height)
End If
End Sub
' Questa funzione blocca il flusso video
Public Sub StopVideo()
If Me.IsDisposed Then
Throw New Exception("Questo oggetto è stato eliminato. E' necessario istanziare un nuovo oggetto.")
End If
Dim errh As Integer = MyMediaControl.Pause()
DsError.ThrowExceptionForHR(errh)
End Sub
' Questa funzione dealloca l'oggetto
Public Sub Dispose()
If Me.IsDisposed Then
Throw New Exception("Questo oggetto è stato eliminato. E' necessario istanziare un nuovo oggetto.")
End If
MyMediaControl.Stop()
Finalize()
IsDisposed = True
End Sub
' Pulitura e rilascio degli oggetti COM
Protected Overrides Sub Finalize()
Marshal.ReleaseComObject(MyGraphBuilder)
Marshal.ReleaseComObject(MyCaptureGraphBuilder)
Marshal.ReleaseComObject(MyWinDevice)
Marshal.ReleaseComObject(MyHardware)
Marshal.ReleaseComObject(Ref_Moniker)
MyBase.Finalize()
End Sub
End Class
End Class
Per completezza, (e per velocizzare l'utilizzo del nostro wrap) dobbiamo aggiungere un ulteriore metodo alla classe Base_VideoUtilty vista nel post precedente.
Il metodo Create_VideoAcquireHardware(Integer) accetterà l'indice della periferica che vogliamo usare e ritornerà la classe Base_VideoAcquireHardware associata a quest'ultima.
' Questa funzione ritorna un oggetto di tipo Base_VideoAcquireHardware che implementa
' alcune semplici operazioni.
Public Function Create_VideoAcquireHardware(ByVal HardwareIndex As Integer) As Base_VideoAcquireHardware
Try
Dim toReturn As Base_VideoAcquireHardware = Nothing
Dim MyMon As Dictionary(Of Integer, IMoniker) = Get_ListOfVideoAcquireHardware()
If MyMon.ContainsKey(HardwareIndex) Then
toReturn = New Base_VideoAcquireHardware(HardwareIndex, CType(MyMon(HardwareIndex), IMoniker))
Else
Throw New Exception("Indice periferica non valido.")
End If
Return toReturn
Catch
Throw New Exception("Cannot create VideoAcquireHardware. (bad index?) Check : " & Err.Description)
End Try
End Function
Il codice riportato in questo post è completo facendo copia e incolla può essere tranquillamente compilato.
Nel prossimo post vedremo un esempio di utilizzo con il link per il download della classe compilata.