Skip to content

Commit

Permalink
Fixed causing AE when reopen capture device on V4L2. #9
Browse files Browse the repository at this point in the history
  • Loading branch information
kekyo committed Nov 19, 2022
1 parent ec7d556 commit 8bcf2a5
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 39 deletions.
121 changes: 84 additions & 37 deletions FlashCap.Core/Devices/V4L2Device.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@

using FlashCap.Internal;
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

using static FlashCap.Internal.NativeMethods_V4L2;
using static FlashCap.Internal.V4L2.NativeMethods_V4L2_Interop;

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

private readonly TimestampCounter counter = new();

private string devicePath;
private bool transcodeIfYUV;
private FrameProcessor frameProcessor;

private long frameIndex;

private readonly IntPtr[] pBuffers = new IntPtr[BufferCount];
private readonly int[] bufferLength = new int[BufferCount];

private int fd;
private IntPtr pBih;
private IntPtr[] pBuffers = new IntPtr[BufferCount];
private int[] bufferLength = new int[BufferCount];
private Thread thread;
private int abortrfd;
private int abortwfd;
Expand Down Expand Up @@ -139,8 +143,8 @@ internal V4L2Device()
$"FlashCap: Couldn't map video buffer: Code={code}, DevicePath={this.devicePath}");
}

pBuffers[index] = pBuffer;
bufferLength[index] = (int)buffer.length;
this.pBuffers[index] = pBuffer;
this.bufferLength[index] = (int)buffer.length;

if (ioctl(fd, Interop.VIDIOC_QBUF, buffer) < 0)
{
Expand Down Expand Up @@ -188,6 +192,17 @@ internal V4L2Device()
}
catch
{
for (var index = 0; index < pBuffers.Length; index++)
{
if (this.pBuffers[index] != default &&
this.bufferLength[index] != default)
{
munmap(this.pBuffers[index], (ulong)this.bufferLength[index]);
this.pBuffers[index] = default;
this.bufferLength[index] = default;
}
}

close(fd);
throw;
}
Expand All @@ -210,9 +225,9 @@ internal V4L2Device()
}

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

this.thread.Join(); // TODO: awaiting
close(this.abortwfd);
this.abortwfd = -1;
}
}
Expand Down Expand Up @@ -244,12 +259,12 @@ code switch
new pollfd
{
fd = this.abortrfd,
events = POLLBITS.POLLIN,
events = POLLBITS.POLLIN | POLLBITS.POLLHUP | POLLBITS.POLLRDHUP | POLLBITS.POLLERR,
},
new pollfd
{
fd = this.fd,
events = POLLBITS.POLLIN,
events = POLLBITS.POLLIN | POLLBITS.POLLHUP | POLLBITS.POLLRDHUP | POLLBITS.POLLERR,
}
};
var buffer = Interop.Create_v4l2_buffer();
Expand All @@ -260,12 +275,8 @@ code switch
{
while (true)
{
var result = poll(fds, fds.Length, -1);
if (result == 0)
{
break;
}
if (result != 1)
var pr = poll(fds, fds.Length, -1);
if (pr < 0)
{
var code = Marshal.GetLastWin32Error();
if (code == EINTR)
Expand All @@ -280,43 +291,79 @@ code switch
throw new ArgumentException(
$"FlashCap: Couldn't get fd status: Code={code}, DevicePath={this.devicePath}");
}

if (ioctl(this.fd, Interop.VIDIOC_DQBUF, buffer) < 0)
if (pr >= 1)
{
// Couldn't get, maybe discarding.
if (Marshal.GetLastWin32Error() is { } code && IsIgnore(code))
if ((fds[0].revents & POLLBITS.POLLIN) == POLLBITS.POLLIN)
{
continue;
break;
}
throw new ArgumentException(
$"FlashCap: Couldn't dequeue video buffer: Code={code}, DevicePath={this.devicePath}");
}

this.frameProcessor.OnFrameArrived(
this,
this.pBuffers[buffer.index],
(int)buffer.bytesused,
// buffer.timestamp is untrustworthy.
this.counter.ElapsedMicroseconds,
this.frameIndex++);

if (ioctl(this.fd, Interop.VIDIOC_QBUF, buffer) < 0)
{
// Couldn't get, maybe discarding.
if (Marshal.GetLastWin32Error() is { } code && IsIgnore(code))
if ((fds[1].revents & POLLBITS.POLLIN) == POLLBITS.POLLIN)
{
continue;
if (ioctl(this.fd, Interop.VIDIOC_DQBUF, buffer) < 0)
{
// Couldn't get, maybe discarding.
if (Marshal.GetLastWin32Error() is { } code && IsIgnore(code))
{
continue;
}

throw new ArgumentException(
$"FlashCap: Couldn't dequeue video buffer: Code={code}, DevicePath={this.devicePath}");
}

try
{
this.frameProcessor.OnFrameArrived(
this,
this.pBuffers[buffer.index],
(int)buffer.bytesused,
// buffer.timestamp is untrustworthy.
this.counter.ElapsedMicroseconds,
this.frameIndex++);
}
// DANGER: Stop leaking exception around outside of unmanaged area...
catch (Exception ex)
{
Trace.WriteLine(ex);
}

if (ioctl(this.fd, Interop.VIDIOC_QBUF, buffer) < 0)
{
// Couldn't get, maybe discarding.
if (Marshal.GetLastWin32Error() is { } code && IsIgnore(code))
{
continue;
}

throw new ArgumentException(
$"FlashCap: Couldn't enqueue video buffer: Code={code}, DevicePath={this.devicePath}");
}
}
throw new ArgumentException(
$"FlashCap: Couldn't enqueue video buffer: Code={code}, DevicePath={this.devicePath}");
}
}
}
catch (Exception ex)
{
Trace.WriteLine(ex);
}
finally
{
for (var index = 0; index < pBuffers.Length; index++)
{
if (this.pBuffers[index] != default &&
this.bufferLength[index] != default)
{
munmap(this.pBuffers[index], (ulong)this.bufferLength[index]);
this.pBuffers[index] = default;
this.bufferLength[index] = default;
}
}

close(this.abortrfd);
close(this.fd);

NativeMethods.FreeMemory(this.pBih);

this.abortrfd = -1;
this.fd = -1;
this.pBih = IntPtr.Zero;
Expand Down
3 changes: 1 addition & 2 deletions FlashCap.Core/Internal/NativeMethods_V4L2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using FlashCap.Internal.V4L2;
using FlashCap.Utilities;
Expand Down Expand Up @@ -158,7 +157,7 @@ public struct pollfd

[DllImport("libc", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern int poll(
pollfd[] fds, int nfds, int timeout);
[In, Out] pollfd[] fds, int nfds, int timeout);

[Flags]
public enum PROT
Expand Down

0 comments on commit 8bcf2a5

Please sign in to comment.