Skip to content

Commit 8bcf2a5

Browse files
committed
Fixed causing AE when reopen capture device on V4L2. #9
1 parent ec7d556 commit 8bcf2a5

File tree

2 files changed

+85
-39
lines changed

2 files changed

+85
-39
lines changed

FlashCap.Core/Devices/V4L2Device.cs

+84-37
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99

1010
using FlashCap.Internal;
1111
using System;
12+
using System.Diagnostics;
1213
using System.Runtime.CompilerServices;
1314
using System.Runtime.InteropServices;
1415
using System.Threading;
1516
using System.Threading.Tasks;
17+
1618
using static FlashCap.Internal.NativeMethods_V4L2;
1719
using static FlashCap.Internal.V4L2.NativeMethods_V4L2_Interop;
1820

@@ -23,16 +25,18 @@ public sealed class V4L2Device : CaptureDevice
2325
private const int BufferCount = 2;
2426

2527
private readonly TimestampCounter counter = new();
28+
2629
private string devicePath;
2730
private bool transcodeIfYUV;
2831
private FrameProcessor frameProcessor;
2932

3033
private long frameIndex;
3134

35+
private readonly IntPtr[] pBuffers = new IntPtr[BufferCount];
36+
private readonly int[] bufferLength = new int[BufferCount];
37+
3238
private int fd;
3339
private IntPtr pBih;
34-
private IntPtr[] pBuffers = new IntPtr[BufferCount];
35-
private int[] bufferLength = new int[BufferCount];
3640
private Thread thread;
3741
private int abortrfd;
3842
private int abortwfd;
@@ -139,8 +143,8 @@ protected override unsafe Task OnInitializeAsync(
139143
$"FlashCap: Couldn't map video buffer: Code={code}, DevicePath={this.devicePath}");
140144
}
141145

142-
pBuffers[index] = pBuffer;
143-
bufferLength[index] = (int)buffer.length;
146+
this.pBuffers[index] = pBuffer;
147+
this.bufferLength[index] = (int)buffer.length;
144148

145149
if (ioctl(fd, Interop.VIDIOC_QBUF, buffer) < 0)
146150
{
@@ -188,6 +192,17 @@ protected override unsafe Task OnInitializeAsync(
188192
}
189193
catch
190194
{
195+
for (var index = 0; index < pBuffers.Length; index++)
196+
{
197+
if (this.pBuffers[index] != default &&
198+
this.bufferLength[index] != default)
199+
{
200+
munmap(this.pBuffers[index], (ulong)this.bufferLength[index]);
201+
this.pBuffers[index] = default;
202+
this.bufferLength[index] = default;
203+
}
204+
}
205+
191206
close(fd);
192207
throw;
193208
}
@@ -210,9 +225,9 @@ protected override Task DisposeAsync(
210225
}
211226

212227
write(this.abortwfd, new byte[] { 0x01 }, 1);
228+
close(this.abortwfd);
213229

214230
this.thread.Join(); // TODO: awaiting
215-
close(this.abortwfd);
216231
this.abortwfd = -1;
217232
}
218233
}
@@ -244,12 +259,12 @@ static bool IsIgnore(int code) =>
244259
new pollfd
245260
{
246261
fd = this.abortrfd,
247-
events = POLLBITS.POLLIN,
262+
events = POLLBITS.POLLIN | POLLBITS.POLLHUP | POLLBITS.POLLRDHUP | POLLBITS.POLLERR,
248263
},
249264
new pollfd
250265
{
251266
fd = this.fd,
252-
events = POLLBITS.POLLIN,
267+
events = POLLBITS.POLLIN | POLLBITS.POLLHUP | POLLBITS.POLLRDHUP | POLLBITS.POLLERR,
253268
}
254269
};
255270
var buffer = Interop.Create_v4l2_buffer();
@@ -260,12 +275,8 @@ static bool IsIgnore(int code) =>
260275
{
261276
while (true)
262277
{
263-
var result = poll(fds, fds.Length, -1);
264-
if (result == 0)
265-
{
266-
break;
267-
}
268-
if (result != 1)
278+
var pr = poll(fds, fds.Length, -1);
279+
if (pr < 0)
269280
{
270281
var code = Marshal.GetLastWin32Error();
271282
if (code == EINTR)
@@ -280,43 +291,79 @@ static bool IsIgnore(int code) =>
280291
throw new ArgumentException(
281292
$"FlashCap: Couldn't get fd status: Code={code}, DevicePath={this.devicePath}");
282293
}
283-
284-
if (ioctl(this.fd, Interop.VIDIOC_DQBUF, buffer) < 0)
294+
if (pr >= 1)
285295
{
286-
// Couldn't get, maybe discarding.
287-
if (Marshal.GetLastWin32Error() is { } code && IsIgnore(code))
296+
if ((fds[0].revents & POLLBITS.POLLIN) == POLLBITS.POLLIN)
288297
{
289-
continue;
298+
break;
290299
}
291-
throw new ArgumentException(
292-
$"FlashCap: Couldn't dequeue video buffer: Code={code}, DevicePath={this.devicePath}");
293-
}
294-
295-
this.frameProcessor.OnFrameArrived(
296-
this,
297-
this.pBuffers[buffer.index],
298-
(int)buffer.bytesused,
299-
// buffer.timestamp is untrustworthy.
300-
this.counter.ElapsedMicroseconds,
301-
this.frameIndex++);
302-
303-
if (ioctl(this.fd, Interop.VIDIOC_QBUF, buffer) < 0)
304-
{
305-
// Couldn't get, maybe discarding.
306-
if (Marshal.GetLastWin32Error() is { } code && IsIgnore(code))
300+
if ((fds[1].revents & POLLBITS.POLLIN) == POLLBITS.POLLIN)
307301
{
308-
continue;
302+
if (ioctl(this.fd, Interop.VIDIOC_DQBUF, buffer) < 0)
303+
{
304+
// Couldn't get, maybe discarding.
305+
if (Marshal.GetLastWin32Error() is { } code && IsIgnore(code))
306+
{
307+
continue;
308+
}
309+
310+
throw new ArgumentException(
311+
$"FlashCap: Couldn't dequeue video buffer: Code={code}, DevicePath={this.devicePath}");
312+
}
313+
314+
try
315+
{
316+
this.frameProcessor.OnFrameArrived(
317+
this,
318+
this.pBuffers[buffer.index],
319+
(int)buffer.bytesused,
320+
// buffer.timestamp is untrustworthy.
321+
this.counter.ElapsedMicroseconds,
322+
this.frameIndex++);
323+
}
324+
// DANGER: Stop leaking exception around outside of unmanaged area...
325+
catch (Exception ex)
326+
{
327+
Trace.WriteLine(ex);
328+
}
329+
330+
if (ioctl(this.fd, Interop.VIDIOC_QBUF, buffer) < 0)
331+
{
332+
// Couldn't get, maybe discarding.
333+
if (Marshal.GetLastWin32Error() is { } code && IsIgnore(code))
334+
{
335+
continue;
336+
}
337+
338+
throw new ArgumentException(
339+
$"FlashCap: Couldn't enqueue video buffer: Code={code}, DevicePath={this.devicePath}");
340+
}
309341
}
310-
throw new ArgumentException(
311-
$"FlashCap: Couldn't enqueue video buffer: Code={code}, DevicePath={this.devicePath}");
312342
}
313343
}
314344
}
345+
catch (Exception ex)
346+
{
347+
Trace.WriteLine(ex);
348+
}
315349
finally
316350
{
351+
for (var index = 0; index < pBuffers.Length; index++)
352+
{
353+
if (this.pBuffers[index] != default &&
354+
this.bufferLength[index] != default)
355+
{
356+
munmap(this.pBuffers[index], (ulong)this.bufferLength[index]);
357+
this.pBuffers[index] = default;
358+
this.bufferLength[index] = default;
359+
}
360+
}
361+
317362
close(this.abortrfd);
318363
close(this.fd);
364+
319365
NativeMethods.FreeMemory(this.pBih);
366+
320367
this.abortrfd = -1;
321368
this.fd = -1;
322369
this.pBih = IntPtr.Zero;

FlashCap.Core/Internal/NativeMethods_V4L2.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System;
1111
using System.Collections.Generic;
1212
using System.Diagnostics;
13-
using System.Net.NetworkInformation;
1413
using System.Runtime.InteropServices;
1514
using FlashCap.Internal.V4L2;
1615
using FlashCap.Utilities;
@@ -158,7 +157,7 @@ public struct pollfd
158157

159158
[DllImport("libc", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
160159
public static extern int poll(
161-
pollfd[] fds, int nfds, int timeout);
160+
[In, Out] pollfd[] fds, int nfds, int timeout);
162161

163162
[Flags]
164163
public enum PROT

0 commit comments

Comments
 (0)