@@ -34,6 +34,9 @@ public sealed class AVFoundationDevice : CaptureDevice
3434 private AVCaptureSession ? session ;
3535 private FrameProcessor ? frameProcessor ;
3636 private IntPtr bitmapHeader ;
37+ private VideoBufferHandler ? videoBufferHandler ;
38+
39+ private GCHandle ? videoBufferHandlerHandle ;
3740
3841 public AVFoundationDevice ( string uniqueID , string modelID ) :
3942 base ( uniqueID , modelID )
@@ -42,39 +45,50 @@ public AVFoundationDevice(string uniqueID, string modelID) :
4245 }
4346
4447 protected override async Task OnDisposeAsync ( )
45- {
46- // Ensure that we stop the session if it's running
47- if ( this . session is not null && IsRunning )
48- {
49- this . session . StopRunning ( ) ;
50- IsRunning = false ;
51- }
48+ {
49+ try
50+ {
51+ // Ensure that we stop the session if it's running
52+ if ( session != null && IsRunning )
53+ {
54+ session . StopRunning ( ) ;
55+ IsRunning = false ;
56+ }
57+
58+ // Clean up the video buffer handler if it exists
59+ if ( videoBufferHandlerHandle . HasValue )
60+ {
61+ videoBufferHandlerHandle . Value . Free ( ) ;
62+ videoBufferHandlerHandle = null ;
63+ }
64+
65+ // Now dispose of the session and other resources
66+ if ( session != null )
67+ {
68+ session . Dispose ( ) ;
69+ session = null ;
70+ }
71+ device ? . Dispose ( ) ; device = null ;
72+ deviceInput ? . Dispose ( ) ; deviceInput = null ;
73+ deviceOutput ? . Dispose ( ) ; deviceOutput = null ;
74+ queue ? . Dispose ( ) ; queue = null ;
5275
53- this . session ? . Dispose ( ) ;
54- this . deviceInput ? . Dispose ( ) ;
55- this . deviceOutput ? . Dispose ( ) ;
56- this . device ? . Dispose ( ) ;
57- this . queue ? . Dispose ( ) ;
58-
59- this . session = null ;
60- this . deviceInput = null ;
61- this . deviceOutput = null ;
62- this . device = null ;
63- this . queue = null ;
64-
65- if ( this . bitmapHeader != IntPtr . Zero )
66- {
67- Marshal . FreeHGlobal ( this . bitmapHeader ) ;
68- this . bitmapHeader = IntPtr . Zero ;
69- }
76+ if ( bitmapHeader != IntPtr . Zero )
77+ {
78+ NativeMethods . FreeMemory ( bitmapHeader ) ;
79+ bitmapHeader = IntPtr . Zero ;
80+ }
7081
71- if ( this . frameProcessor is not null )
82+ if ( frameProcessor != null )
83+ {
84+ await frameProcessor . DisposeAsync ( ) . ConfigureAwait ( false ) ;
85+ frameProcessor = null ;
86+ }
87+ }
88+ finally
7289 {
73- await this . frameProcessor . DisposeAsync ( ) . ConfigureAwait ( false ) ;
74- this . frameProcessor = null ;
90+ await base . OnDisposeAsync ( ) . ConfigureAwait ( false ) ;
7591 }
76-
77- await base . OnDisposeAsync ( ) . ConfigureAwait ( false ) ;
7892 }
7993
8094 protected override Task OnInitializeAsync ( VideoCharacteristics characteristics , TranscodeFormats transcodeFormat ,
@@ -142,14 +156,21 @@ format.FormatDescription.Dimensions is var dimensions &&
142156 {
143157 var validPixelFormat = this . deviceOutput . AvailableVideoCVPixelFormatTypes . FirstOrDefault ( p => p == pixelFormatType ) ;
144158 this . deviceOutput . SetPixelFormatType ( validPixelFormat ) ;
159+ this . deviceOutput . SetVideoOutputSize ( characteristics . Width , characteristics . Height , validPixelFormat ) ;
145160 }
146161 else
147162 {
148163 // Fallback to the mapped pixel format if no available list is provided
149164 this . deviceOutput . SetPixelFormatType ( pixelFormatType ) ;
150165 }
151-
152- this . deviceOutput . SetSampleBufferDelegate ( new VideoBufferHandler ( this ) , this . queue ) ;
166+
167+ videoBufferHandler = new VideoBufferHandler ( this ) ;
168+
169+ this . deviceOutput . SetSampleBufferDelegate ( videoBufferHandler , this . queue ) ;
170+
171+ // Protect against GC moving the delegate
172+ videoBufferHandlerHandle = GCHandle . Alloc ( videoBufferHandler ) ;
173+
153174 this . deviceOutput . AlwaysDiscardsLateVideoFrames = true ;
154175 }
155176 finally
@@ -174,31 +195,80 @@ format.FormatDescription.Dimensions is var dimensions &&
174195 catch
175196 {
176197 NativeMethods . FreeMemory ( this . bitmapHeader ) ;
198+ this . bitmapHeader = IntPtr . Zero ;
199+
200+ this . queue ? . Dispose ( ) ;
201+ this . queue = null ;
202+ this . device ? . Dispose ( ) ;
203+ this . device = null ;
204+ this . deviceInput ? . Dispose ( ) ;
205+ this . deviceInput = null ;
206+ this . deviceOutput ? . Dispose ( ) ;
207+ this . deviceOutput = null ;
208+
177209 throw ;
178210 }
179211 }
180212
181213 protected override Task OnStartAsync ( CancellationToken ct )
182214 {
183- this . session ? . StartRunning ( ) ;
184- return TaskCompat . CompletedTask ;
215+ try
216+ {
217+ if ( session == null )
218+ throw new InvalidOperationException ( "Session is null" ) ;
219+ this . session ? . StartRunning ( ) ;
220+ this . IsRunning = true ;
221+ return TaskCompat . CompletedTask ;
222+ } catch ( Exception ex )
223+ {
224+ Debug . WriteLine ( $ "Error starting session: { ex . Message } ") ;
225+ throw new InvalidOperationException ( "Failed to start the capture session." , ex ) ;
226+ }
227+
185228 }
186229
187230 protected override Task OnStopAsync ( CancellationToken ct )
188231 {
189- this . session ? . StopRunning ( ) ;
232+ try
233+ {
234+ if ( session == null )
235+ throw new InvalidOperationException ( "Session is null" ) ;
236+ if ( this . IsRunning )
237+ {
238+ this . session ? . StopRunning ( ) ;
239+ this . IsRunning = false ;
240+
241+ }
242+
243+ } catch ( Exception ex )
244+ {
245+ Debug . WriteLine ( $ "Error stopping session: { ex . Message } ") ;
246+ throw new InvalidOperationException ( "Failed to stop the capture session." , ex ) ;
247+ }
190248 return TaskCompat . CompletedTask ;
191249 }
192250
193251 protected override void OnCapture ( IntPtr pData , int size , long timestampMicroseconds , long frameIndex , PixelBuffer buffer )
194252 {
195- buffer . CopyIn ( this . bitmapHeader , pData , size , timestampMicroseconds , frameIndex , TranscodeFormats . Auto ) ;
253+ try
254+ {
255+ if ( this . bitmapHeader == IntPtr . Zero ) return ;
256+ if ( pData == IntPtr . Zero || size <= 0 )
257+ {
258+ throw new ArgumentException ( "Invalid pixel data or size." ) ;
259+ }
260+ buffer . CopyIn ( this . bitmapHeader , pData , size , timestampMicroseconds , frameIndex , TranscodeFormats . Auto ) ;
261+ } catch ( Exception ex )
262+ {
263+ Debug . WriteLine ( $ "Error capturing frame: { ex . Message } ") ;
264+ throw new InvalidOperationException ( "Failed to capture frame." , ex ) ;
265+ }
196266 }
197267
198268 internal sealed class VideoBufferHandler : AVCaptureVideoDataOutputSampleBuffer
199269 {
200270 private readonly AVFoundationDevice device ;
201- private int frameIndex ;
271+ private int frameIndex = 0 ;
202272
203273 public VideoBufferHandler ( AVFoundationDevice device )
204274 {
0 commit comments